Commit Graph

248 Commits

Author SHA1 Message Date
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
SomberNight
7834f6c427 commands: fix satoshis decimal conversion in payto cmd and others
When called via jsonrpc (but not via cli) with non-string amounts,
there could be a rounding error resulting in sending 1 sat less.

example:
```
$ ./run_electrum --testnet -w ~/.electrum/testnet/wallets/test_segwit_2 paytomany '[["tb1q6k5h4cz6ra8nzhg90xm9wldvadgh0fpttfthcg", 0.00033389]]' --fee 0
02000000000101b9e6018acb16952e3c9618b069df404dc85544eda8120e5f6e7cd7e94ce5ae8d0100000000fdffffff02fd8100000000000016001410c5b97085ec1637a9f702852f5a81f650fae1566d82000000000000160014d5a97ae05a1f4f315d0579b6577daceb5177a42b024730440220251d2ce83f6e69273de8e9be8602fbcf72b9157e1c0116161fa52f7e04db6e4302202d84045cc6b7056a215d1db3f59884e28dadd5257e1a3960068f90df90b452d1012102b0eff3bf364a2ab5effe952cba33521ebede81dac88c71951a5ed598cb48347b3a022500

$ curl --data-binary '{"id":"curltext","method":"paytomany","params":{"outputs":[["tb1q6k5h4cz6ra8nzhg90xm9wldvadgh0fpttfthcg", 0.00033389]], "fee": 0, "wallet": "/home/user/.electrum/testnet/wallets/test_segwit_2"}}' http://user:pass@127.0.0.1:7777
{"id": "curltext", "jsonrpc": "2.0", "result": "02000000000101b9e6018acb16952e3c9618b069df404dc85544eda8120e5f6e7cd7e94ce5ae8d0100000000fdffffff02fe8100000000000016001410c5b97085ec1637a9f702852f5a81f650fae1566c82000000000000160014d5a97ae05a1f4f315d0579b6577daceb5177a42b0247304402206ef66b845ca298c14dc6e8049cba9ed19db1671132194518ce5d521de6f5df8802205ca4b1aee703e3b98331fb9b88210917b385560020c8b2a8a88da38996b101c4012102b0eff3bf364a2ab5effe952cba33521ebede81dac88c71951a5ed598cb48347b39022500"}
```
^ note that first tx has output for 0.00033389, second tx has output for 0.00033388

fixes https://github.com/spesmilo/electrum/issues/8274
2023-03-22 12:22:36 +00:00
SomberNight
e14ed717a8 qml: fix paying bip70 invoices 2023-03-14 16:32:14 +00:00
SomberNight
9e0d7b61bb util.format_time: trivial clean-up 2023-03-13 15:03:36 +00:00
SomberNight
7584ba00ce wallet: kill negative conf numbers for TxMinedInfo
fixes https://github.com/spesmilo/electrum/issues/8240

#8240 was triggering an AssertionError in wallet.get_invoice_status,
as code there was assuming conf >= 0. To trigger, force-close
a LN channel, and while the sweep is waiting on the CSV, try to
make a payment in the Send tab to the ismine change address used
for the sweep in the future_tx. (order of events can also be reversed)
2023-03-09 14:59:08 +00:00
SomberNight
d4338fb503 tests: clean-up use of asyncio 2023-02-20 16:53:44 +00:00
SomberNight
8a4c06b692 swaps: small refactor and add unit tests for claim tx 2023-02-17 14:04:03 +00:00
SomberNight
373db76ac9 util: kill bh2u
no longer useful, and the name is so confusing...
2023-02-17 11:43:11 +00:00
Jan Sarenik
9ef9b9b768 Add ex.signet.bublina.. option for signet explorer 2023-02-05 10:30:09 +01:00
ThomasV
7625b4e63b follow-up d6febb5c12 2023-01-26 11:13:35 +01:00
ThomasV
d6febb5c12 Display mined tx outputs as ShortIDs instead of full transaction outpoints.
ShortIDs were originally designed for lightning channels, and are now
understood by some block explorers.

This allows to remove one column in the UTXO tab (height is redundant).

In the transaction dialog, the space saving ensures that all inputs fit
into one line (it was not the case previously with p2wsh addresses).
For clarity and consistency, the ShortID is displayed for both inputs
and outputs in the transaction dialog.
2023-01-26 10:48:28 +01:00
myxmaster
4d4d2e2206 allow for translation of age strings 2023-01-18 10:51:17 +01:00
ThomasV
7d52021d6b Merge pull request #8139 from SomberNight/202301_locale_decimal_point
locale amounts: consistently use "." as dec point, and " " as thou sep
2023-01-13 16:00:47 +01:00
SomberNight
f3102af195 util: create_and_start_event_loop() to wait until loop starts
With a PyCharm debugger attached, sometimes the python process is so
CPU-starved for me that create_and_start_event_loop() returned
before the event loop actually started, resulting in weird errors.
I guess this could happen even without a debugger attached on a
sufficiently slow CPU.

```
...\electrum\electrum\wallet.py:3580: in restore_wallet_from_text
    wallet = Wallet(db, storage, config=config)
...\electrum\electrum\wallet.py:3501: in __new__
    wallet = WalletClass(db, storage, config=config)
...\electrum\electrum\wallet.py:3345: in __init__
    Deterministic_Wallet.__init__(self, db, storage, config=config)
...\electrum\electrum\wallet.py:3135: in __init__
    self.synchronize()
...\electrum\electrum\wallet.py:3283: in synchronize
    count += self.synchronize_sequence(False)
...\electrum\electrum\wallet.py:3267: in synchronize_sequence
    self.create_new_address(for_change)
...\electrum\electrum\wallet.py:3254: in create_new_address
    self.adb.add_address(address)
...\electrum\electrum\address_synchronizer.py:213: in add_address
    self.up_to_date_changed()
...\electrum\electrum\address_synchronizer.py:680: in up_to_date_changed
    util.trigger_callback('adb_set_up_to_date', self)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <electrum.util.CallbackManager object at 0x000002B1788AD6F0>
event = 'adb_set_up_to_date'
args = (<electrum.address_synchronizer.AddressSynchronizer object at 0x000002B17A687670>,)

    def trigger_callback(self, event, *args):
        """Trigger a callback with given arguments.
        Can be called from any thread. The callback itself will get scheduled
        on the event loop.
        """
        if self.asyncio_loop is None:
            self.asyncio_loop = get_asyncio_loop()
>           assert self.asyncio_loop.is_running(), "event loop not running"
E           AssertionError: event loop not running

...\electrum\electrum\util.py:1734: AssertionError
```
2023-01-12 18:19:00 +00:00
SomberNight
2a9909c252 locale amounts: consistently use "." as dec point, and " " as thou sep
Always use "." as decimal point, and " " as thousands separator.

Previously,
- for decimal point, we were using
  - "." in some places (e.g. AmountEdit, most fiat amounts), and
  - `locale.localeconv()['decimal_point']` in others.
- for thousands separator, we were using
  - "," in some places (most fiat amounts), and
  - " " in others (format_satoshis)

I think it is better to be consistent even if whatever we pick differs from the locale.
Using whitespace for thousands separator (vs comma) is probably less confusing for people
whose locale would user "." for ts and "," for dp (as in e.g. German).

The alternative option would be to always use the locale. Even if we decide to do that later,
this refactoring should be useful.

closes https://github.com/spesmilo/electrum/issues/2629
2023-01-10 14:45:35 +00:00
SomberNight
2e06a7992e util: improve Tor SOCKS detection
see https://github.com/spesmilo/electrum/issues/7317#issuecomment-1369281075

I guess this should be at least as reliable as the old method -
however the old method would log an error for the Tor process,
and this does not.
Suggested by Tor contributor: trinity-1686a.
2023-01-03 14:49:57 +00:00
SomberNight
253150cb36 qt network dialog: don't poll Tor socks proxy, but scan on interaction
Polling is introduces spam in Tor logs.
Also, Tor Browser 12.0 apparently has a bug where our polling renders
the socks proxy unusuable after some time.
see https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/41549

Instead of trying to detect a Tor socks proxy every 10 seconds, we now
run detection when the Qt network dialog gets opened, and also when
the user switches to the "Proxy" tab in the dialog.

fixes https://github.com/spesmilo/electrum/issues/7317
2023-01-02 11:58:16 +00:00
SomberNight
21dac9f8a6 util.EventListener: don't log "registering callback"
IMO this is just too spammy to log in the general case when running with -v
2022-11-06 06:12:41 +00:00
SomberNight
21c18bbbe7 util.parse_URI: honour docstring and only raise InvalidBitcoinURI
follow-up ac1d53f067
2022-11-03 12:47:21 +00:00
SomberNight
ac1d53f067 util.parse_URI: allow bip21 with lightning key without fallback addr
We would reject bip21 URIs that contained both an "address=" and a "lightning=" key with a bolt11 invoice,
where the bolt11 invoice did not contain a fallback address.
2022-10-31 19:03:12 +00:00
SomberNight
12e628e2e6 aiorpcx: fix bug in timeout_after monkeypatch
fixes https://github.com/spesmilo/electrum/issues/7952

see in particular https://github.com/spesmilo/electrum/issues/7952#issuecomment-1227225602
> So the issue is with the aiorpcx monkey patch in util.py, as it
> relies on side-effecting the asyncio.Task, and it patches Task.cancel.
> However, aiohttp also uses Task.cancel for its own timeouting of the
> http request, with the same Task object, and this confuses timeout_after.
> Ultimately FxThread.run exits.

related https://github.com/kyuupichan/aiorpcX/pull/47

---

note: I am not content at all with this monkey-patching approach,
but at the same time I don't see how to properly fix this handling all
edge-cases in aiorpcx.

python 3.11 is finally adding an implementation of TaskGroup [0] and
an async timeout context manager [1] in the asyncio module of the stdlib.
Looking at the implementations, they look unfeasible to backport:
much of the implementation of asyncio.Task had to be changed for them
to work, and TaskGroup in particular relies on the new ExceptionGroups.
Some of these edge cases we are battling with aiorpcx.curio look
difficult to fix without those stdlib changes...
Anyway, when we bump the min python to 3.11, I look forward to switching
to that code instead, and ripping this stuff out.

[0]: https://docs.python.org/3.11/library/asyncio-task.html#task-groups
[1]: https://docs.python.org/3.11/library/asyncio-task.html#asyncio.timeout
2022-08-26 15:27:53 +00:00
SomberNight
abef454237 qt: better handle unparseable URIs
fixes https://github.com/spesmilo/electrum/issues/7941
2022-08-16 15:27:13 +00:00
avirgovi
b5d2b3c512 create chmod aware of XDG_RUNTIME_DIR
closes https://github.com/spesmilo/electrum/pull/7681
related https://github.com/spesmilo/electrum/issues/6334

Co-authored-by: avirgovi <avirgovi@cisco.com>
Co-authored-by: SomberNight <somber.night@protonmail.com>
2022-08-09 19:04:17 +02:00
SomberNight
233d2373df util: implement is_subpath 2022-08-09 18:48:21 +02:00
SomberNight
28fe345b0b keystore.check_password: raise better exc if called on pwless ks
If keystore.check_password is called with some pw on a keystore that does not have a password set,
it now raises better exceptions: it should now always raise InvalidPassword, and with a nicer msg.
Previously the exc type would depend on the ks type.

Examples before change:

```
>>> wallet.keystore.check_password("asd")
Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/keystore.py", line 580, in check_password
    xprv = pw_decode(self.xprv, password, version=self.pw_hash_version)
  File "/home/user/wspace/electrum/electrum/crypto.py", line 311, in pw_decode
    plaintext_bytes = pw_decode_bytes(data, password, version=version)
  File "/home/user/wspace/electrum/electrum/crypto.py", line 270, in pw_decode_bytes
    data_bytes = bytes(base64.b64decode(data))
  File "/usr/lib/python3.10/base64.py", line 87, in b64decode
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
```

```
>>> wallet.keystore.check_password("asd")
Traceback (most recent call last):
    s = aes_decrypt_with_iv(secret, iv, e)
  File "/home/user/wspace/electrum/electrum/crypto.py", line 157, in aes_decrypt_with_iv
    data = decryptor.update(data) + decryptor.finalize()
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/primitives/ciphers/base.py", line 148, in finalize
    data = self._ctx.finalize()
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 193, in finalize
    raise ValueError(
ValueError: The length of the provided data is not a multiple of the block length.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/gui/qt/console.py", line 254, in exec_command
    result = eval(command, self.namespace, self.namespace)
  File "<string>", line 1, in <module>
  File "/home/user/wspace/electrum/electrum/keystore.py", line 248, in check_password
    self.get_private_key(pubkey, password)
  File "/home/user/wspace/electrum/electrum/keystore.py", line 267, in get_private_key
    sec = pw_decode(self.keypairs[pubkey], password, version=self.pw_hash_version)
  File "/home/user/wspace/electrum/electrum/crypto.py", line 311, in pw_decode
    plaintext_bytes = pw_decode_bytes(data, password, version=version)
  File "/home/user/wspace/electrum/electrum/crypto.py", line 271, in pw_decode_bytes
    return _pw_decode_raw(data_bytes, password, version=version)
  File "/home/user/wspace/electrum/electrum/crypto.py", line 255, in _pw_decode_raw
    raise InvalidPassword() from e
electrum.util.InvalidPassword: Incorrect password
```

-----

Examples after change:
```
>>> wallet.keystore.check_password("asd")
Traceback (most recent call last):
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "...\electrum\keystore.py", line 68, in wrapper
    return check_password_fn(self, password)
  File "...\electrum\keystore.py", line 605, in check_password
    xprv = pw_decode(self.xprv, password, version=self.pw_hash_version)
  File "...\electrum\crypto.py", line 311, in pw_decode
    plaintext_bytes = pw_decode_bytes(data, password, version=version)
  File "...\electrum\crypto.py", line 267, in pw_decode_bytes
    raise CiphertextFormatError("ciphertext not valid base64") from e
electrum.crypto.CiphertextFormatError: ciphertext not valid base64

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "...\electrum\gui\qt\console.py", line 254, in exec_command
    result = eval(command, self.namespace, self.namespace)
  File "<string>", line 1, in <module>
  File "...\electrum\keystore.py", line 76, in wrapper
    raise InvalidPassword("password given but keystore has no password") from e
electrum.util.InvalidPassword: password given but keystore has no password
```

```
>>> wallet.keystore.check_password("asd")
Traceback (most recent call last):
    s = aes_decrypt_with_iv(secret, iv, e)
  File "...\electrum\crypto.py", line 158, in aes_decrypt_with_iv
    data = cipher.decrypt(data)
  File "...\Python310\site-packages\Cryptodome\Cipher\_mode_cbc.py", line 246, in decrypt
    raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "...\electrum\keystore.py", line 68, in wrapper
    return check_password_fn(self, password)
  File "...\electrum\keystore.py", line 272, in check_password
    self.get_private_key(pubkey, password)
  File "...\electrum\keystore.py", line 291, in get_private_key
    sec = pw_decode(self.keypairs[pubkey], password, version=self.pw_hash_version)
  File "...\electrum\crypto.py", line 311, in pw_decode
    plaintext_bytes = pw_decode_bytes(data, password, version=version)
  File "...\electrum\crypto.py", line 268, in pw_decode_bytes
    return _pw_decode_raw(data_bytes, password, version=version)
  File "...\electrum\crypto.py", line 249, in _pw_decode_raw
    raise InvalidPassword() from e
electrum.util.InvalidPassword: Incorrect password

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "...\electrum\gui\qt\console.py", line 254, in exec_command
    result = eval(command, self.namespace, self.namespace)
  File "<string>", line 1, in <module>
  File "...\electrum\keystore.py", line 76, in wrapper
    raise InvalidPassword("password given but keystore has no password") from e
electrum.util.InvalidPassword: password given but keystore has no password
```
2022-07-12 15:50:45 +02:00
SomberNight
e72f575eea android build: factor out package name ("org.electrum.electrum")
to make it easier to change, to make it easy to have a co-existing mainnet and testnet install
(or two mainnet installs, etc)
2022-07-09 04:47:32 +02:00
SomberNight
649cad0122 lnurl: clean-up 2022-06-29 16:18:23 +02:00
bitromortac
fe2fbbd9b1 add lnurl-pay and lightning address support
* bundles all payment identifiers into handle_payment_identifier
* adds lnurl decoding
* adds lightning address decoding
2022-06-29 16:18:15 +02:00
SomberNight
81880e8538 EventListener: key global dict by modulepath+clsname instead of clsname
follow-up fe74e4b9c9

Just for sanity -- there could be a collision of class names (defined in separate modules).
```
>>> wallet.is_up_to_date.__qualname__
'Abstract_Wallet.is_up_to_date'
>>> wallet.is_up_to_date.__module__
'electrum.wallet'
```
2022-06-27 17:23:08 +02:00
ThomasV
fe74e4b9c9 EventListener: pre-register event listeners in the decorator,
and use that registration in the iterator.

That way, we do not invoke getattr on arbitrary methods, which
could trigger unwanted code execution (for example, if a method
is decorated with @property, getattr will execute its code).
2022-06-23 09:11:16 +02:00
SomberNight
5b000a871f EventListener follow-ups: adapt left-out classes and minor clean-ups 2022-06-22 02:09:26 +02:00
ThomasV
dbf055de9a EventListener class to handle callbacks
and QtEventListener for Qt
2022-06-22 02:07:46 +02:00
SomberNight
b6de15b95d util.profiler: make log line easier to understand 2022-06-09 19:46:42 +02:00