1
0
Commit Graph

139 Commits

Author SHA1 Message Date
Sander van Grieken
6f653af3ff whitespace, imports 2025-06-03 11:26:23 +02:00
ThomasV
972ebb0420 Daemon: do not set config.CURRENT_WALLET to None
Instead, raise a UserFacingException if the daemon has several wallets in memory.
2025-06-03 10:41:03 +02:00
ThomasV
9e225d1269 Replace config GUI_LAST_WALLET with CURRENT_WALLET
- CURRENT_WALLET is set when a single wallet is loaded in memory, and it
   remains set after Electrum stops running.
 - If several wallets are loaded at the same time, CURRENT_WALLET is unset,
   and RPCs must specify the wallet explicitly (using --wallet for the CLI)
 - The fallback to 'default_wallet' essentially only applies when
   creating a new wallet file
2025-06-02 14:05:53 +02:00
ghost43
fc13001bb9 Merge pull request #9875 from SomberNight/202505_pad_protocol_3
interface: add padding and some noise to protocol messages
2025-05-30 15:37:12 +00:00
SomberNight
6257d9e266 constants.py: add datadir_subdir, cli_flag, config_key methods
- use these to generalise recurring "switch-like" ifs
- this effectively also adds a `--mainnet` CLI option
    - closes https://github.com/spesmilo/electrum/issues/9790
2025-05-29 18:41:28 +00:00
SomberNight
447052b4ff interface: add padding and some noise to protocol messages
basic countermeasures against traffic analysis
2025-05-29 17:29:30 +00:00
f321x
8870838834 raise instead of overwriting the config file on syntax error 2025-05-15 17:21:16 +02:00
SomberNight
351cc6abd9 Revert "interface: add padding and some noise to protocol messages"
Unforeseen issues. Needs more work..

This reverts commit 097eabed1f.
2025-05-08 18:34:07 +00:00
SomberNight
097eabed1f interface: add padding and some noise to protocol messages
basic countermeasures against traffic analysis
2025-05-08 14:35:44 +00:00
SomberNight
2600a3bc74 requirements: bump max aiorpcx
and bump pinned aiorpcx and electrum-aionostr
2025-05-08 14:31:11 +00:00
Sander van Grieken
b12206482a android: remove setting SWAPSERVER_URL in config at startup for android.
This config var should have never been saved to the config file, therefore it is safe to remove
instead of setting to '' to overwrite any existing value.
2025-05-01 09:53:35 +02:00
SomberNight
d7bc6cbb3c add harden_memory_linux: harder for other processes to read our memory
This module tries to restrict the ability of other processes to access the memory of our process.
Traditionally, on Linux, one process can access the memory of another arbitrary process
if both are running as the same user (uid). (Root can ofc access the memory of ~any process)
Programs can opt-out from this by setting `prctl(PR_SET_DUMPABLE, 0);`

also see https://man.archlinux.org/man/PR_SET_DUMPABLE.2const.en

-----

Also, from https://unix.stackexchange.com/a/518452 :

In a terminal window:
```
% echo $$  # show our pid
6744
% read -sp 'secret password: '; echo
secret password:
%
```
Then in another terminal window:
```
% grep heap /proc/6744/maps
01bb7000-01c3e000 rw-p 00000000 00:00 0                                  [heap]
% dd if=/proc/6744/mem bs=1 skip=$((0x01bb7000)) count=$((0x01c3e000-0x01bb7000)) status=none |
    strings | less
...
% dd if=/proc/6744/mem bs=1 skip=$((0x01bb7000)) count=$((0x01c3e000-0x01bb7000)) status=none |
    strings | grep obiwan
obiwan_kenobi  # "secret password"
```
2025-04-18 00:54:30 +00:00
ThomasV
9d1ffe0c37 support for arguments to plugin commands:
- add a simple parser that only figures out where the config is,
   and does not complain if args are unknown
2025-03-16 15:01:22 +01:00
ThomasV
cb39737a39 Plugins call with cmd_only:
- pass temporary config to Plugins
 - load only enabled plugins
 - parse the command line again after plugins are loaded
2025-03-15 13:31:00 +01:00
ThomasV
4f79e516e4 run_electrum: add parse_command_line method 2025-03-15 13:22:28 +01:00
f321x
ae64583ebc add handling of plugin commands 2025-03-15 13:22:28 +01:00
SomberNight
5b2068976c config: remove a footgun 2025-02-07 16:22:44 +00:00
f321x
171aa5ee5a bump aiorpcx version 2025-01-20 12:11:55 +01:00
Sander van Grieken
897327da5a android: support regtest builds in a similar fashion to testnet builds, by package name. 2025-01-17 15:37:51 +01:00
SomberNight
fb5a1af666 bump min required Python version to 3.10 2025-01-10 18:52:49 +00:00
ThomasV
60f13a977e Swaps over Nostr
- Separation between SwapManager and its transport:
   Legacy transpport uses http, Nostr uses websockets
 - The transport uses a context to open/close connections.
   This context is not async, because it needs to be called
   from the GUI
 - Swapserver fees values are initialized to None instead
   of 0, so that any attempt to use them before the swap
   manager is initialized will raise an exception.
 - Remove swapserver fees disk caching (swap_pairs file)
 - Regtests use http transport
 - Android uses http transport (until QML is ready)
2024-11-12 09:32:25 +01:00
Sander van Grieken
974ed6828e run_electrum: allow running from git clone to override DeprecationWarning warnings 2024-10-30 11:35:17 +01:00
SomberNight
214e04d4bb hw plugins: cmdline: fix offline commands for encrypted hw wallets
```
$ ./run_electrum --testnet signmessage -w /home/user/.electrum/testnet/wallets/test_trezor_white_bip84 tb1q5pguna9y2g9y2gsu8r8gmxeye2cefvyly8dg02 heyheyhey -o
  0.84 | W | plugins.jade.jadepy.jade | No module named 'electrum.plugins.jade.jadepy.jade_ble'
  0.84 | W | plugins.jade.jadepy.jade | BLE scanning/connectivity will not be available
  3.73 | E | __main__ | error running command (without daemon)
Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/plugins/trezor/clientbase.py", line 151, in get_xpub
    node = trezorlib.btc.get_public_node(self.client, address_n).node
  File "/home/user/.local/lib/python3.10/site-packages/trezorlib/tools.py", line 274, in wrapped_f
    ret = f(*args, **kwargs)
  File "/home/user/.local/lib/python3.10/site-packages/trezorlib/btc.py", line 125, in get_public_node
    return client.call(
  File "/home/user/.local/lib/python3.10/site-packages/trezorlib/tools.py", line 297, in wrapped_f
    return f(client, *args, **kwargs)
  File "/home/user/.local/lib/python3.10/site-packages/trezorlib/client.py", line 260, in call
    resp = self._callback_pin(resp)
  File "/home/user/.local/lib/python3.10/site-packages/trezorlib/client.py", line 186, in _callback_pin
    pin = self.ui.get_pin(msg.type)
  File "/home/user/wspace/electrum/electrum/plugins/trezor/clientbase.py", line 308, in get_pin
    pin = self.handler.get_pin(msg.format(self.device), show_strength=show_strength)
AttributeError: 'NoneType' object has no attribute 'get_pin'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/user/wspace/electrum/./run_electrum", line 540, 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 227, in run_offline_command
    password = get_password_for_hw_device_encrypted_storage(plugins)
  File "/home/user/wspace/electrum/./run_electrum", line 212, in get_password_for_hw_device_encrypted_storage
    return client.get_password_for_storage_encryption()
  File "/home/user/wspace/electrum/electrum/plugin.py", line 523, in wrapper
    return run_in_hwd_thread(partial(func, *args, **kwargs))
  File "/home/user/wspace/electrum/electrum/plugin.py", line 516, in run_in_hwd_thread
    return 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 "/usr/lib/python3.10/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/user/wspace/electrum/electrum/plugins/hw_wallet/plugin.py", line 260, in get_password_for_storage_encryption
    xpub = self.get_xpub(derivation, "standard")
  File "/home/user/wspace/electrum/electrum/plugin.py", line 523, in wrapper
    return run_in_hwd_thread(partial(func, *args, **kwargs))
  File "/home/user/wspace/electrum/electrum/plugin.py", line 513, in run_in_hwd_thread
    return func()
  File "/home/user/wspace/electrum/electrum/plugins/trezor/clientbase.py", line 150, in get_xpub
    with self.run_flow(creating_wallet=creating):
  File "/home/user/wspace/electrum/electrum/plugins/trezor/clientbase.py", line 89, in __exit__
    self.end_flow()
  File "/home/user/wspace/electrum/electrum/plugins/trezor/clientbase.py", line 82, in end_flow
    self.handler.finished()
AttributeError: 'NoneType' object has no attribute 'finished'
```
2024-10-10 21:25:34 +00:00
SomberNight
1257f21b1b constants: rm set_testnet/set_mainnet fns with AbstractNet.set_as_network 2024-09-16 15:27:45 +00:00
wakiyamap
1d9ff40d0b Add suport testnet4 2024-09-14 03:52:03 +09:00
SomberNight
5f95d919df requirements: bump pinned (and max) aiorpcx versions to 0.23.1
related https://github.com/spesmilo/electrum/issues/8954
2024-04-08 16:44:59 +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
0e5a1380ca i18n: simplify language default. only translate string if using GUI. 2024-01-16 17:23:43 +00:00
ThomasV
68159b3ef6 walletDB: replace 'manual_upgrades' parameter with 'upgrade', with opposite semantics 2023-09-22 15:02:07 +02:00
ThomasV
4e80ef818d Add unlock command to CLI (stores wallet password in memory) 2023-09-15 15:37:53 +02:00
SomberNight
b3514672d6 run_electrum: small clean-up in init_cmdline 2023-09-08 15:46:06 +00:00
SomberNight
b45c84f24f remove the kivy gui
We now use the qml gui on Android, and haven't been maintaining
the kivy GUI for a while.
2023-08-30 16:47:37 +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
ThomasV
b96cc82333 Make storage a field of db
This comes from the jsonpatch_new branch.
I rather have in master now, because it touches a lot of filese.
2023-08-18 08:08:31 +02:00
ThomasV
0ebb6469ff CLI: make sure we do not load WalletDB while another process is accessing it
- do not load WalletDB in order to check keystore password
- do not allow --offline commands while a daemon is running
2023-08-18 08:05:54 +02:00
SomberNight
44ef5a35b7 CLI: fix regression re handling "unknown command", re locale
if qt is not installed, e.g. on a server, was getting:
```
$ ./run_electrum sadasdasddsa
Traceback (most recent call last):
  File "/home/user/wspace/electrum/./run_electrum", line 532, in <module>
    main()
  File "/home/user/wspace/electrum/./run_electrum", line 383, in main
    lang = get_default_language(gui_name=gui_name)
  File "/home/user/wspace/electrum/electrum/gui/default_lang.py", line 23, in get_default_language
    from PyQt5.QtCore import QLocale
ModuleNotFoundError: No module named 'PyQt5.QtCore'
```
2023-08-09 14:52:40 +00:00
SomberNight
20f4d44f09 cli: "daemon -d": init rpc credentials before os.fork()
follow-up 8b195ee77a
2023-08-03 22:42:08 +00:00
SomberNight
8b195ee77a cli: "./run_electrum daemon -d" to block until daemon becomes ready
Without this,
`$ python3 -m unittest electrum.tests.regtest.TestUnixSockets.test_unixsockets`
was failing on my machine but succeeding on CI, due to timing differences.
2023-08-03 17:21:05 +00:00
Sander van Grieken
db6779bf04 qt: show send tab if payment identifier is passed on the cmdline 2023-07-07 20:49:45 +02:00
Sander van Grieken
a2ca191de1 pass wallet to PaymentIdentifier instead of config and contacts 2023-06-28 16:49:28 +02: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
SomberNight
3ab47e1c45 (trivial) convert more config keys 2023-06-05 15:29:35 +00:00
SomberNight
ccc012674f unconditionally raise ImportError if asserts are disabled
I have reconsidered and now think that we should always hard-fail
if asserts asserts are disabled. It is just easier to reason about
the code knowing that asserts are evaluated.

If an end-user or library user has a concrete use case where this is
a problem, please open an issue and let us know.

follow-up
0f541be6f1
0e5464ca13
2023-06-01 17:34:32 +00: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
SomberNight
68eaa680f8 CLI/RPC: better error msg when running daemon on Windows
`-d` is not supported, due to missing os.fork

related: https://github.com/spesmilo/electrum/issues/5511
2023-05-16 12:28:59 +00:00
SomberNight
a0c43573ab locale/i18n: get default language and set it as early as possible
TODO elaborate xxxxx
2023-05-05 17:00:18 +00:00
SomberNight
0e5464ca13 android build: enable asserts, and add sanity-check for it
Note that 0f541be6f1 added a warning log if asserts are disabled.
It is intentional that these two things are in separate files:
We always want to log that warning, even if someone is using electrum as a library.
However, in that latter case, I think it's fine not to sys.exit(), but leave the decision up to the library user.

Similar thinking when running from source: let's log the warning but don't sys.exit().
2023-03-31 16:24:14 +00:00
SomberNight
1530668960 qt/qml: delay starting network until after first-start-network-setup
The qt, qml, and kivy GUIs have a first-start network-setup screen
that allows the user customising the network settings before creating a wallet.
Previously the daemon used to create the network and start it, before this screen,
before the GUI even starts. If the user changed network settings, those would
be set on the already running network, potentially including restarting the network.

Now it becomes the responsibility of the GUI to start the network, allowing this
first-start customisation to take place before starting the network at all.
The qt and the qml GUIs are adapted to make use of this. Kivy, and the other
prototype GUIs are not adapted and just start the network right away, as before.
2023-03-30 00:59:02 +00:00
SomberNight
cb11e1faed CLI: make "electrum stop" robust to dead daemon / lingering lockfile
follow-up fbf79b148b
2023-01-25 15:35:42 +00:00
ThomasV
fbf79b148b CLI: poor man's detection of already running daemon. 2023-01-14 09:56:18 +01:00