1
0
Commit Graph

299 Commits

Author SHA1 Message Date
SomberNight
5084f75724 util.make_dir: handle multi-process race
When launching an electrum daemon and an electrum cli command quickly after each other,
in a test using a random fresh datadir, they both try to create the same folder hierarchy
and race on the "if not exist: mkdir" commands.
2025-05-23 15:49:11 +00:00
Sander van Grieken
84322500ef lnworker: imports, formatting, whitespace 2025-05-23 14:00:57 +02:00
f321x
f7ad95f42d make lightning dns seed fetching async 2025-05-16 17:10:10 +02:00
SomberNight
ba3783f998 refactor qt.util.ChoiceWidget: introduce ChoiceItem 2025-05-06 18:12:37 +00:00
f321x
ee7d2ee17d validate and deduplicate relay config input in qt gui
Adds validation and deduplication of the relay urls entered in the QT
settings dialog. This is supposed to prevent malformed or duplicated
relay entries.
Also resets the relays to the default value if no (valid) url
is entered. This prevents the user from getting stuck without relays
(otherwise the user would have to research for relay urls manually if
they don't know any).
2025-05-02 09:03:27 +02:00
ThomasV
8bb94dd57d follow-up previous commit 2025-04-22 09:41:17 +02:00
ThomasV
69f8176aab Qt: show relative locktime in tx details 2025-04-22 09:30:53 +02:00
f321x
253ab6849a implement NIP47 plugin 2025-04-10 10:22:29 +02:00
SomberNight
220e6a1ef9 Merge branch 'pr/9717': re asyncio.Event.set() race conditions
see https://github.com/spesmilo/electrum/pull/9717
plus local follow-up clean-up
2025-04-08 19:56:38 +00:00
SomberNight
4b9d874d13 util: add fn run_sync_function_on_asyncio_thread
note: the return value is not propagated out.
It would be trivial to do that for the block=True case - but what about block=False?
2025-04-08 19:53:49 +00:00
SomberNight
70d1e1170e asyncio: clarify strong refs for run_coroutine_threadsafe
We added some code in 0b3a283586
to explicitly hold strong refs for all tasks/futures. At the time I was uncertain if that also solves
GC issues with asyncio.run_coroutine_threadsafe.
ref https://github.com/spesmilo/electrum/pull/9608#issuecomment-2703681663

Looks like it does. run_coroutine_threadsafe *is* going through the custom task factory.
See the unit test.
The somewhat confusing thing is that we need a few event loop iterations for the task factory to run,
due to how run_coroutine_threadsafe is implemented. And also, the task that we will hold as strong ref
in the global set is not the concurrent.futures.Future that run_coroutine_threadsafe returns.

So this commit simply "fixes" the unit test so that it showcases this, and removes related, older, plumbing
from util.py that we now know is no longer needed because of this.
2025-04-08 18:54:58 +00:00
SomberNight
5c233ac325 ci: enable more flake8 stuff
```
$ export ELECTRUM_LINTERS=E9,E101,E129,E273,E274,E703,E71,E722,F5,F6,F7,F8,W191,W29,B
$ export ELECTRUM_LINTERS_IGNORE=B007,B009,B010,B019,B036,F541,F841
$ flake8 . --count --select="$ELECTRUM_LINTERS" --ignore="$ELECTRUM_LINTERS_IGNORE" --show-source --statistics --exclude "*_pb2.py,electrum/_vendor/"
./electrum/commands.py:98:1: F811 redefinition of unused 'format_satoshis' from line 48
def format_satoshis(x):
^
./electrum/commands.py:437:9: F811 redefinition of unused 'Mnemonic' from line 62
        from .mnemonic import Mnemonic
        ^
./electrum/gui/qt/wizard/wallet.py:37:5: F811 redefinition of unused 'Daemon' from line 14
    from electrum.daemon import Daemon
    ^
./electrum/lntransport.py:14:1: F811 redefinition of unused 'Optional' from line 12
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
^
./electrum/lntransport.py:14:1: F811 redefinition of unused 'TYPE_CHECKING' from line 12
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
^
./electrum/plugin.py:966:13: F811 redefinition of unused 'hid' from line 593
            import hid
            ^
./electrum/plugin.py:1040:13: F811 redefinition of unused 'hid' from line 593
            import hid
            ^
./electrum/util.py:44:1: F811 redefinition of unused 'json' from line 26
import json
^
./electrum/util.py:46:1: F811 redefinition of unused 'NamedTuple' from line 29
from typing import NamedTuple, Optional
^
./electrum/util.py:46:1: F811 redefinition of unused 'Optional' from line 29
from typing import NamedTuple, Optional
^
./electrum/util.py:1456:56: F811 redefinition of unused 'traceback' from line 34
        async def __aexit__(self, exc_type, exc_value, traceback):
                                                       ^
./electrum/wallet_db.py:536:9: F811 redefinition of unused 'LOCAL' from line 46
        LOCAL = 1
        ^
./electrum/wallet_db.py:537:9: F811 redefinition of unused 'REMOTE' from line 46
        REMOTE = -1
        ^
./tests/test_bitcoin.py:28:1: F811 redefinition of unused 'bitcoin' from line 9
from electrum import crypto, constants, bitcoin
^
./tests/test_txbatcher.py:11:1: F811 redefinition of unused 'Transaction' from line 7
from electrum.transaction import Transaction, PartialTxInput, PartialTxOutput, TxOutpoint
^
./tests/test_wallet_vertical.py:20:1: F811 redefinition of unused 'Transaction' from line 10
from electrum.transaction import Transaction, PartialTxOutput, tx_from_any, Sighash
^
16    F811 redefinition of unused 'format_satoshis' from line 48
16

```
2025-04-02 16:21:59 +00:00
f321x
2290650faa pass proxy settings to aionostr Manager
rebase on master
2025-03-10 10:57:30 +01:00
SomberNight
0b3a283586 asyncio: hold our own strong refs for tasks and futures
see https://docs.python.org/3.13/library/asyncio-task.html#asyncio.create_task :

> Important
>
> Save a reference to the result of this function, to avoid a task
> disappearing mid-execution. The event loop only keeps weak references
> to tasks. A task that isn’t referenced elsewhere may get garbage
> collected at any time, even before it’s done. For reliable
> “fire-and-forget” background tasks, gather them in a collection

ref https://github.com/python/cpython/issues/91887
ref https://github.com/beeware/toga/pull/2814
2025-03-05 17:06:04 +00:00
SomberNight
5dc2ae243e util: refactor Tor-detection to be async
- on my PC, with Tor Browser running (socks proxy on port 9150), detect_tor_socks_proxy took ~4.01 seconds
- this was because we probed port 9050, 2 sec timeout, 9051, 2 sec timeout, 9150, ~few ms to succeed
- instead we now probe all ports concurrently
2025-03-05 14:46:47 +00:00
Sander van Grieken
fea598cfbe network: create ProxySettings class replacing dict and encapsulating proxy related funcs,
allow enable/disable proxy without nuking proxy mode, host and port (explicit enable_proxy config setting),
move tor probe from frontend to backend code, add probe buttons for Qt and QML
2025-03-04 14:23:33 +01:00
ThomasV
392c219913 simplify history-related commands:
- reduce number of methods
 - use nametuples instead of dicts
 - only two types: OnchainHistoryItem and LightningHistoryItem
 - channel open/closes are groups
 - move capital gains into separate RPC
2025-02-19 11:40:21 +01:00
f321x
a47421490d increase nonce size to 32 byte and make it hex in event 2025-02-12 10:09:11 +01:00
f321x
947094c1b0 add pow, more default relays, new event type 2025-02-11 18:16:15 +01:00
SomberNight
be2cd02e54 some clean-ups now that we require python 3.10 2025-01-10 18:52:53 +00:00
bitromortac
ea584e13fc anchors: switch to zero-fee-htlcs
* sets the weight of htlc transactions to zero, thereby putting a zero
  fee for the htlc transactions
* add inputs to htlc-tx for fee bumping
* switches feature flags
* disable anchor test vectors, which are now partially invalid
2024-11-25 10:56:50 +01:00
WakiyamaP
3d375116e9 add testnet4 explorer(wakiyamap.dev) 2024-11-11 11:55:04 +09:00
accumulator
1b9f1dbb7f Merge pull request #9250 from accumulator/network_tor_stream_isolation
network: use TOR stream isolation
2024-10-28 09:50:00 +01:00
SomberNight
5c81f77b5d util: add docstring to EventListener 2024-10-25 16:58:47 +00:00
Sander van Grieken
f4520b9e0d network: use TOR stream isolation
also refactor, for proxy instantiation, use Network instance, not a proxy dict.
2024-10-25 01:10:58 +02:00
wakiyamap
1d9ff40d0b Add suport testnet4 2024-09-14 03:52:03 +09:00
SomberNight
bec78b4210 util.error_text_str_to_safe_str: truncate long errors
The messages are sometimes logged and sometimes shown to the user,
- for logging we might not want to truncate or have higher limits,
- but when shown to the user, we definitely want to truncate the error text.
It is simplest to just do the truncation here, at the lowest level.

Note that we usually prepend the error text with a header e.g. "[DO NOT TRUST THIS MESSAGE]"
and if the error text is too long, this header at the beginning might get "lost" in some way.
Hence we should truncate the error text.
2024-06-17 16:52:13 +00:00
SomberNight
13e2949088 rm some legacy cruft for old python versions 2024-06-05 14:55:48 +00:00
SomberNight
0866581b2c daemon error-handling: fix traceback.format_exception() on old python
The new API for traceback.format_exception was only added in python 3.10 (91e93794d5).
2024-06-05 14:45:28 +00:00
SomberNight
af2c9b081c util: add AsyncHangDetector, and use it for lnpeer._process_message 2024-06-03 18:36:08 +00:00
SomberNight
e25658d724 fix plot.py
fixes https://github.com/spesmilo/electrum/issues/9058
2024-05-22 15:26:26 +00:00
SomberNight
bd492fbd14 cli/rpc: nicer error messages and error-passing
Previously, generally, in case of any error, commands would raise a generic "Exception()" and the CLI/RPC would convert that and return it as `str(e)`.

With this change, we now distinguish "user-facing exceptions" (e.g. "Password required" or "wallet not loaded") and "internal errors" (e.g. bugs).
- for "user-facing exceptions", the behaviour is unchanged
- for "internal errors", we now pass around the traceback (e.g. from daemon server to rpc client) and show it to the user (previously, assuming there was a daemon running, the user could only retrieve the exception from the log of that daemon). These errors use a new jsonrpc error code int (code 2).

As the logic only changes for "internal errors", I deem this change not to be compatibility-breaking.

----------
Examples follow.
Consider the following two commands:
```
@command('')
async def errorgood(self):
	from electrum.util import UserFacingException
	raise UserFacingException("heyheyhey")

@command('')
async def errorbad(self):
	raise Exception("heyheyhey")
```

----------
(before change)

CLI with daemon:
```
$ ./run_electrum --testnet daemon -d
starting daemon (PID 9221)
$ ./run_electrum --testnet errorgood
heyheyhey
$ ./run_electrum --testnet errorbad
heyheyhey
$ ./run_electrum --testnet stop
Daemon stopped
```

CLI without daemon:
```
$ ./run_electrum --testnet -o errorgood
heyheyhey
$ ./run_electrum --testnet -o errorbad
heyheyhey
```

RPC:
```
$ curl --data-binary '{"id":"curltext","jsonrpc":"2.0","method":"errorgood","params":[]}' http://user:pass@127.0.0.1:7777
{"id": "curltext", "jsonrpc": "2.0", "error": {"code": 1, "message": "heyheyhey"}}
$ curl --data-binary '{"id":"curltext","jsonrpc":"2.0","method":"errorbad","params":[]}' http://user:pass@127.0.0.1:7777
{"id": "curltext", "jsonrpc": "2.0", "error": {"code": 1, "message": "heyheyhey"}}
```

----------
(after change)

CLI with daemon:
```
$ ./run_electrum --testnet daemon -d
starting daemon (PID 9254)
$ ./run_electrum --testnet errorgood
heyheyhey
$ ./run_electrum --testnet errorbad
(inside daemon): Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/daemon.py", line 254, in handle
    response['result'] = await f(*params)
  File "/home/user/wspace/electrum/electrum/daemon.py", line 361, in run_cmdline
    result = await func(*args, **kwargs)
  File "/home/user/wspace/electrum/electrum/commands.py", line 163, in func_wrapper
    return await func(*args, **kwargs)
  File "/home/user/wspace/electrum/electrum/commands.py", line 217, in errorbad
    raise Exception("heyheyhey")
Exception: heyheyhey

internal error while executing RPC
$ ./run_electrum --testnet stop
Daemon stopped
```

CLI without daemon:
```
$ ./run_electrum --testnet -o errorgood
heyheyhey
$ ./run_electrum --testnet -o errorbad
  0.78 | E | __main__ | error running command (without daemon)
Traceback (most recent call last):
  File "/home/user/wspace/electrum/./run_electrum", line 534, in handle_cmd
    result = fut.result()
  File "/usr/lib/python3.10/concurrent/futures/_base.py", line 458, in result
    return self.__get_result()
  File "/usr/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result
    raise self._exception
  File "/home/user/wspace/electrum/./run_electrum", line 255, in run_offline_command
    result = await func(*args, **kwargs)
  File "/home/user/wspace/electrum/electrum/commands.py", line 163, in func_wrapper
    return await func(*args, **kwargs)
  File "/home/user/wspace/electrum/electrum/commands.py", line 217, in errorbad
    raise Exception("heyheyhey")
Exception: heyheyhey
```

RPC:
```
$ curl --data-binary '{"id":"curltext","jsonrpc":"2.0","method":"errorgood","params":[]}' http://user:pass@127.0.0.1:7777
{"id": "curltext", "jsonrpc": "2.0", "error": {"code": 1, "message": "heyheyhey"}}
$ curl --data-binary '{"id":"curltext","jsonrpc":"2.0","method":"errorbad","params":[]}' http://user:pass@127.0.0.1:7777
{"id": "curltext", "jsonrpc": "2.0", "error": {"code": 2, "message": "internal error while executing RPC", "data": {"exception": "Exception('heyheyhey')", "traceback": "Traceback (most recent call last):\n  File \"/home/user/wspace/electrum/electrum/daemon.py\", line 254, in handle\n    response['result'] = await f(*params)\n  File \"/home/user/wspace/electrum/electrum/commands.py\", line 163, in func_wrapper\n    return await func(*args, **kwargs)\n  File \"/home/user/wspace/electrum/electrum/commands.py\", line 217, in errorbad\n    raise Exception(\"heyheyhey\")\nException: heyheyhey\n"}}}
```
2024-02-12 19:02:02 +00:00
SomberNight
a083b95021 util.CallbackManager: handle callbacks being cancelled
was getting log spam when running the pycharm debugger in certain cases:
```
  3.29 | E | concurrent.futures | exception calling callback for <Future at 0x1d4987bcc70 state=cancelled>
Traceback (most recent call last):
  File "...\Python310\lib\concurrent\futures\_base.py", line 342, in _invoke_callbacks
    callback(self)
  File "...\electrum\electrum\util.py", line 1785, in on_done
    if exc := fut_.exception():
  File "...\Python310\lib\concurrent\futures\_base.py", line 485, in exception
    raise CancelledError()
concurrent.futures._base.CancelledError
```
2024-02-09 13:48:51 +00:00
SomberNight
0b7e52fbbe gui: label tx sizes as "vbytes" and feerates as "sat/vbyte"
closes https://github.com/spesmilo/electrum/issues/6961
2024-02-05 16:27:27 +00:00
SomberNight
a9a8ed2eb4 follow-up: factor out more hardcoded "sat/byte" and "sat/b" strings
- rename globals
- also rm hardcoded strings from qml
- use consistent unit names in qml
  (previously mixed sat/vB and sat/byte (latter coming from core lib))
2024-02-03 05:26:31 +00:00
SomberNight
13a421aabb factor out hardcoded "sat/byte" and "sat/b" strings
Though note that the qml GUI has some more in qml/js context.
2024-02-03 04:18:46 +00:00
SomberNight
d54184dbc1 fix some DeprecationWarnings in python3.12
...\electrum\electrum\logging.py:137: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
...\electrum\electrum\x509.py:310: DeprecationWarning: datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.fromtimestamp(timestamp, datetime.UTC).
2023-12-24 08:57:57 +00:00
accumulator
134fd6c656 Merge pull request #8714 from accumulator/tor_probe
network: async tor probe
2023-12-04 16:45:03 +01:00
SomberNight
64f82cd260 qt wizard bip39 recovery: better handle --offline mode
```
 32.40 | E | gui.qt.exception_window.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/gui/qt/wizard/wallet.py", line 709, in <lambda>
    button.clicked.connect(lambda: Bip39RecoveryDialog(self, get_account_xpub, on_account_select))
  File "/home/user/wspace/electrum/electrum/gui/qt/bip39_recovery_dialog.py", line 40, in __init__
    fut = asyncio.run_coroutine_threadsafe(coro, network.asyncio_loop)
AttributeError: 'NoneType' object has no attribute 'asyncio_loop'
```
2023-12-01 17:37:58 +00:00
SomberNight
03778b6e0c add FIXMEs to instances where we don't use the network proxy
so that it at least does not get forgotten

related https://github.com/spesmilo/electrum/issues/4754
2023-11-30 19:31:14 +00:00
Sander van Grieken
90d1d587e8 network: async tor probe 2023-11-30 14:32:19 +01:00
Sander van Grieken
297568a148 network: remove resolver monkey-patch as no local hostname lookups are performed when
enabling SOCKS5 proxy.
2023-11-30 14:27:31 +01:00
SomberNight
e5fae1d685 util.NetworkRetryManager: better document API 2023-11-13 12:22:05 +00:00
qqux
7429c29a37 Cleanup block explorers 2023-09-27 23:37:36 +00:00
SomberNight
4c63d8729b add sanity checks we don't sign tx including dummy addr
Somewhat a follow-up to 649ce979ab.

This adds some safety belts so we don't accidentally sign a tx that
contains a dummy address.
Specifically we check that tx does not contain output for dummy addr:
- in wallet.sign_transaction
- in network.broadcast_transaction

The second one is perhaps redundant, but I think it does not hurt.
2023-09-16 04:36:08 +00:00
SomberNight
6a9e532ff1 Plugins: make Plugins.stop() faster
Plugins.stop() is now part of the normal shutdown flow (since 90f39bce88),
so this shaves up to 100 ms off that (avg: 50 ms).
It is also waited on in around ~60 unit tests: this saves 3 sec.
2023-08-24 18:27:34 +00:00
SomberNight
90f39bce88 run_electrum: have daemon manage Plugins object, and call Plugins.stop
Plugins.stop was never called, so the Plugins thread only stopped
because of the is_running() check in run(), which triggers too late:
the Plugins thread was stopping after the main thread stopped.

E.g. playing around in the qt wizard with wallet creation for a Trezor,
and closing the wizard (only window):
``` 24.85 | E | p/plugin.Plugins |
Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/util.py", line 386, in run_jobs
    job.run()
  File "/home/user/wspace/electrum/electrum/plugin.py", line 430, in run
    client.timeout(cutoff)
  File "/home/user/wspace/electrum/electrum/plugin.py", line 363, in wrapper
    return run_in_hwd_thread(partial(func, *args, **kwargs))
  File "/home/user/wspace/electrum/electrum/plugin.py", line 355, in run_in_hwd_thread
    fut = _hwd_comms_executor.submit(func)
  File "/usr/lib/python3.10/concurrent/futures/thread.py", line 167, in submit
    raise RuntimeError('cannot schedule new futures after shutdown')
RuntimeError: cannot schedule new futures after shutdown
```
2023-08-24 17:15:14 +00:00
SomberNight
2c2e3f8ca4 util.randrange: expand docstring 2023-08-16 16:32:42 +00:00
SomberNight
5e1e7dd9b3 util.CallbackManager: make sure exceptions in cbs are logged
this resolves the existing FIXME
2023-08-16 16:27:17 +00:00
SomberNight
d51f00e2a3 asyncio.wait_for() is too buggy. use util.wait_for2() instead
wasted some time because asyncio.wait_for() was suppressing cancellations. [0][1][2]
deja vu... [3]

Looks like this is finally getting fixed in cpython 3.12 [4]
So far away...
In attempt to avoid encountering this again, let's try using
asyncio.timeout in 3.11, which is how upstream reimplemented wait_for in 3.12 [4], and
aiorpcx.timeout_after in 3.8-3.10.

[0] https://github.com/python/cpython/issues/86296
[1] https://bugs.python.org/issue42130
[2] https://bugs.python.org/issue45098
[3] https://github.com/kyuupichan/aiorpcX/issues/44
[4] https://github.com/python/cpython/pull/98518
2023-08-04 18:18:21 +00:00