Commit Graph

280 Commits

Author SHA1 Message Date
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
SomberNight
6977be8e1b util.CallbackManager: keep strong references for running futures
This clears up log spam for regtest tests.

related:
- https://bugs.python.org/issue44665
- https://github.com/python/cpython/issues/88831
- https://textual.textualize.io/blog/2023/02/11/the-heisenbug-lurking-in-your-async-code/
- https://github.com/python/cpython/issues/91887#issuecomment-1434816045
- "Task was destroyed but it is pending!"

Perhaps we should inspect all our usages of
- asyncio.create_task
- loop.create_task
- asyncio.ensure_future
- asyncio.run_coroutine_threadsafe
?

Example log for running a regtest test:
```
$ python3 -m unittest electrum.tests.regtest.TestLightningAB.test_collaborative_close
***** test_collaborative_close ******
initializing alice
--- Logging error ---
Traceback (most recent call last):
  File "/usr/lib/python3.10/logging/__init__.py", line 1100, in emit
    msg = self.format(record)
  File "/usr/lib/python3.10/logging/__init__.py", line 943, in format
    return fmt.format(record)
  File "/home/user/wspace/electrum/electrum/logging.py", line 44, in format
    record = copy.copy(record)  # avoid mutating arg
  File "/usr/lib/python3.10/copy.py", line 92, in copy
    rv = reductor(4)
ImportError: sys.meta_path is None, Python is likely shutting down
Call stack:
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1781, in call_exception_handler
    self._exception_handler(self, context)
  File "/home/user/wspace/electrum/electrum/util.py", line 1535, in on_exception
    loop.default_exception_handler(context)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1744, in default_exception_handler
    logger.error('\n'.join(log_lines), exc_info=exc_info)
Message: "Task was destroyed but it is pending!\ntask: <Task pending name='Task-2' coro=<Abstract_Wallet.on_event_adb_set_up_to_date() running at /home/user/wspace/electrum/electrum/wallet.py:485> wait_for=<Future finished result=0> cb=[_chain_future.<locals>._call_set_state() at /usr/lib/python3.10/asyncio/futures.py:392]>"
Arguments: ()

[--- SNIP --- more of the same --- SNIP ---]

--- Logging error ---
Traceback (most recent call last):
  File "/usr/lib/python3.10/logging/__init__.py", line 1100, in emit
    msg = self.format(record)
  File "/usr/lib/python3.10/logging/__init__.py", line 943, in format
    return fmt.format(record)
  File "/home/user/wspace/electrum/electrum/logging.py", line 44, in format
    record = copy.copy(record)  # avoid mutating arg
  File "/usr/lib/python3.10/copy.py", line 92, in copy
    rv = reductor(4)
ImportError: sys.meta_path is None, Python is likely shutting down
Call stack:
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1781, in call_exception_handler
    self._exception_handler(self, context)
  File "/home/user/wspace/electrum/electrum/util.py", line 1535, in on_exception
    loop.default_exception_handler(context)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 1744, in default_exception_handler
    logger.error('\n'.join(log_lines), exc_info=exc_info)
Message: "Task was destroyed but it is pending!\ntask: <Task pending name='Task-31' coro=<Abstract_Wallet.on_event_adb_set_up_to_date() running at /home/user/wspace/electrum/electrum/wallet.py:485> wait_for=<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.10/asyncio/futures.py:385, Task.task_wakeup()]> cb=[_chain_future.<locals>._call_set_state() at /usr/lib/python3.10/asyncio/futures.py:392]>"
Arguments: ()
true
true
true
true
funding alice
```
2023-07-17 15:33:57 +00:00
Sander van Grieken
4900d01344 allow more time to detect tor 2023-07-13 18:51:48 +02:00
ThomasV
5cf320b2c7 Merge pull request #8462 from spesmilo/payment_identifiers2
Payment identifiers
2023-07-09 10:02:14 +02:00
SomberNight
d65aa3369f daemon: split standardize_path from daemon._wallets keying
- standardize_path is made more lenient: it no longer calls os.path.realpath,
  as that was causing issues on Windows with some mounted drives
- daemon._wallets is still keyed on the old strict standardize_path, but
  filesystem operations in WalletStorage use the new lenient standardize_path.
  - daemon._wallets is strict to forbid opening the same logical file twice concurrently
  - fs operations however work better on the non-resolved paths, so they use those

closes https://github.com/spesmilo/electrum/issues/8495
2023-06-30 11:15:16 +00:00
ThomasV
15eb765eac payment_identifiers:
- this separates GUI from core handling
 - the PaymentIdentifier class handles network requests
 - the GUI is agnostic about the type of PI
2023-06-28 16:49:28 +02:00
ThomasV
295734fc53 storage: encapsulate type conversions of stored objects using
decorators (instead of overloading JsonDB._convert_dict and
 _convert_value)
 - stored_in for elements of a StoreDict
 - stored_as for singletons
 - extra register methods are defined for key conversions

This commit was adapted from the jsonpatch branch
2023-06-18 13:08:57 +02:00
SomberNight
24980feab7 config: introduce ConfigVars
A new config API is introduced, and ~all of the codebase is adapted to it.
The old API is kept but mainly only for dynamic usage where its extra flexibility is needed.

Using examples, the old config API looked this:
```
>>> config.get("request_expiry", 86400)
604800
>>> config.set_key("request_expiry", 86400)
>>>
```

The new config API instead:
```
>>> config.WALLET_PAYREQ_EXPIRY_SECONDS
604800
>>> config.WALLET_PAYREQ_EXPIRY_SECONDS = 86400
>>>
```

The old API operated on arbitrary string keys, the new one uses
a static ~enum-like list of variables.

With the new API:
- there is a single centralised list of config variables, as opposed to
  these being scattered all over
- no more duplication of default values (in the getters)
- there is now some (minimal for now) type-validation/conversion for
  the config values

closes https://github.com/spesmilo/electrum/pull/5640
closes https://github.com/spesmilo/electrum/pull/5649

Note: there is yet a third API added here, for certain niche/abstract use-cases,
where we need a reference to the config variable itself.
It should only be used when needed:
```
>>> var = config.cv.WALLET_PAYREQ_EXPIRY_SECONDS
>>> var
<ConfigVarWithConfig key='request_expiry'>
>>> var.get()
604800
>>> var.set(3600)
>>> var.get_default_value()
86400
>>> var.is_set()
True
>>> var.is_modifiable()
True
```
2023-05-25 17:39:48 +00:00
ThomasV
fda408e4e4 Merge pull request #8444 from Har01d/add-3xpl-as-explorer
Add 3xpl.com to the list of explorers
2023-05-21 11:17:21 +02:00
SomberNight
603088a79f util: simplify profiler 2023-05-19 23:03:27 +00:00
Nikita Zhavoronkov
9f045e546f Add 3xpl.com to the list of explorers 2023-05-20 04:29:44 +06:00
SomberNight
68fb996d20 wallet_db version 52: break non-homogeneous multisig wallets
- case 1: in version 4.4.1, 4.4.2, the qml GUI wizard allowed creating multisig wallets with an old_mpk as cosigner.
- case 2: in version 4.4.0, 4.4.1, 4.4.2, the qml GUI wizard allowed creating multisig wallets with mixed xpub/Ypub/Zpub.

The corresponding missing input validation was a bug in the wizard, it was unintended behaviour. Validation was added in d2cf21fc2b. Note however that there might be users who created such wallet files.

Re case 1 wallet files: there is no version of Electrum that allows spending from such a wallet. Coins received at addresses are not burned, however it is technically challenging to spend them. (unless the multisig can spend without needing the old_mpk cosigner in the quorum).

Re case 2 wallet files: it is possible to create a corresponding spending wallet for such a multisig, however it is a bit tricky. The script type for the addresses in such a heterogeneous xpub wallet is based on the xpub_type of the first keystore. So e.g. given a wallet file [Yprv1, Zpub2] it will have sh(wsh()) scripts, and the cosigner should create a wallet file [Ypub1, Zprv2] (same order).

Technically case 2 wallet files could be "fixed" automatically by converting the xpub types as part of a wallet_db upgrade. However if the wallet files also contain seeds, those cannot be converted ("standard" vs "segwit" electrum seed).
Case 1 wallet files are not possible to "fix" automatically as the cosigner using the old_mpk is not bip32 based.

It is unclear if there are *any* users out there affected by this. I suspect for case 1 it is very likely there are none (not many people have pre-2.0 electrum seeds which were never supported as part of a multisig who would also now try to create a multisig using them); for case 2 however there might be.

This commit breaks both case 1 and case 2 wallets: these wallet files can no longer be opened in new Electrum, an error message is shown and the crash reporter opens. If any potential users opt to send crash reports, at least we will know they exist and can help them recover.
2023-05-11 14:26:11 +00:00
SomberNight
19759281ef qml: follow-up BtcField validator: take base unit fully into account
Not just for the fractional part, but also for the integer part.

follow-up 4df6052567
2023-05-10 13:06:38 +00:00
SomberNight
7f52415807 util: add some logging to NetworkJobOnDefaultServer starting/stopping
to help debug claims of Synchronizer maybe getting stuck
2023-05-02 13:41:47 +00:00
SomberNight
ad5f95cb87 util.profiler: add "min_threshold" arg 2023-04-26 14:49:37 +00:00
SomberNight
312f2641e7 don't use bare except
use "except Exception", or if really needed explicitly "except BaseException"
2023-04-24 12:58:01 +00:00
SomberNight
ea864cd5c9 qml: TxListModel: don't rely on wallet.db.get_transaction() finding tx
tx might get removed from wallet after wallet.get_full_history() but before the model is populated

closes https://github.com/spesmilo/electrum/issues/8339
2023-04-23 16:00:42 +00:00
SomberNight
f528758c29 util: merge time_difference() into age(), make age() localisable
fixes https://github.com/spesmilo/electrum/issues/8304
follow-up 4d4d2e2206
2023-04-16 23:05:22 +00:00
SomberNight
72da9c1a6a sanitise untrusted error bytes before logging it
full-blown paranoia kicking in
2023-04-06 14:28:31 +00:00
SomberNight
8c1fe10f54 qml TxDetails: show short_id instead of block height and txpos
The "TX index" (txpos) item I think was confusing.
2023-03-23 16:57:16 +00:00