An Electrum plugin that reimplements Electrum Personal Server functionality inline, letting you connect Electrum to your own Bitcoin Core full node without running a separate process.
Tested against Electrum's 1.7 protocol branch (PyQt6) and Bitcoin
Core 30 on testnet4.
When the plugin is enabled and configured, it:
- Spawns a local TLS Electrum-protocol server on
127.0.0.1:50002, answering wallet RPCs (blockchain.scripthash.*,blockchain.scriptpubkey.*,blockchain.transaction.*, header subscriptions, broadcast, fee estimation, etc.) directly from your node. - Makes Bitcoin Core watch your wallet's scripts:
- Electrum protocol 1.7 (preferred): as the wallet subscribes to scriptPubKeys, the plugin imports each one into Core on demand. This works for arbitrary addresses, including multisig and hardware-wallet wallets, with no xpub required.
- Protocol ≤ 1.6 (fallback): resolves the wallet's known addresses via a scripthash → address table. Arbitrary addresses outside that set cannot be resolved (a one-way-hash limitation of the older protocol).
- Optionally bulk-imports your wallet's descriptors and triggers a
Core rescan, to surface historical transactions. Single-signature
(
wpkh/sh(wpkh)/pkh) and multisig (wsh/sh(wsh)/shofsortedmulti) wallets are supported, viaimportdescriptors(Core ≥ 0.21) withimportmultifallback for single-sig. - Auto-configures Electrum to use the embedded server (
oneserverplus a server bookmark), so Electrum never talks to any third-party server.
electrum-eps-plugin/
├── eps/ Plugin package — distributed as eps-X.Y.Z.zip
│ ├── manifest.json Plugin metadata
│ ├── __init__.py Config key declarations
│ ├── qt.py BasePlugin subclass, settings UI, server lifecycle
│ ├── rpc.py Synchronous Core JSON-RPC client + cookie auth
│ ├── addresses.py Descriptor building, on-demand ScriptWatcher, bulk import
│ ├── server.py Electrum protocol TCP+TLS server
│ └── tls.py Self-signed certificate generation
├── tests/ unittest suite (no Electrum install required)
└── .github/workflows/
└── release.yml Builds eps-X.Y.Z.zip on tag push
| Thread | Purpose |
|---|---|
| Main Qt thread | GUI, config reads/writes |
eps-server-main |
socket.accept() loop |
eps-client-<peer> |
One per connected Electrum client |
eps-notifier |
Polls Core every 10 s; pushes header + script-status pushes |
ImportWorker (QThread) |
Bulk address import + rescan, reports progress to Qt |
All Bitcoin Core RPC calls are synchronous (blocking). Each client has
its own thread; EPS is single-user by design. Status updates from
background threads to the GUI are marshalled through a Qt signal
(_StatusBridge) so GUI widgets are only ever touched on the Qt thread.
The plugin needs RPC access to a Bitcoin Core node and a dedicated watch-only descriptor wallet to import addresses into.
Either method works (configured in Wallet → EPS Settings):
-
Static credentials — set them in
bitcoin.confand enter them in the RPC Auth field asuser:password:server=1 rpcuser=eps rpcpassword=eps # use something secure outside testing -
Cookie auth — leave RPC Auth blank and set the Directory field to your Core datadir; the plugin reads
<datadir>/<network>/.cookie.Note: if
rpcuser/rpcpasswordare present inbitcoin.conf, Core does not write a.cookiefile, so cookie auth is unavailable — use the static credentials in that case. The cookie is also only readable by the user Core runs as.
bitcoin-cli -testnet4 createwallet eps-test true true "" false true true
# ^disable_private_keys
# ^blank
# ^passphrase
# ^avoid_reuse
# ^descriptors
# ^load_on_startupEnter this wallet's name in the Wallet field. The plugin imports into it on demand (protocol 1.7) and when you click Import addresses from open wallet. Expect the first rescan to take minutes (testnet4) to hours (mainnet; pruned nodes are supported but only scan non-pruned history).
- Download
eps-X.Y.Z.zipfrom the GitHub releases page. - In Electrum: Tools → Plugins → Add → select the zip.
- Authorize with your Electrum plugin password (set on first use).
- Restart Electrum; EPS Settings now appears in the Wallet menu.
Electrum's external plugin manager loads .zip files from the
network-specific plugin directory. Symlinks and bare directories there
are ignored by the loader.
git clone <this repo> ~/repos/electrum-eps-plugin
cd ~/repos/electrum-eps-plugin
# Build and install for testnet4
zip -r eps-0.1.0.zip eps/
cp eps-0.1.0.zip ~/.electrum/testnet4/plugins/
# Restart Electrum and re-authorize (the zip hash changes every build)
~/Downloads/electrum-4.7.2-x86_64.AppImage --testnet4A one-liner for the inner dev loop:
cd ~/repos/electrum-eps-plugin && \
rm -f eps-0.1.0.zip && rm -rf eps/__pycache__ && \
zip -r eps-0.1.0.zip eps/ > /dev/null && \
cp eps-0.1.0.zip ~/.electrum/testnet4/plugins/- Open Wallet → EPS Settings and enter your Bitcoin Core RPC URL, authentication (RPC Auth or a datadir for cookie auth), and the watch-only wallet name.
- Click Save & Connect. The plugin starts the embedded server and
automatically points Electrum at
127.0.0.1:50002. - (First time per wallet, optional) Click Import addresses from open wallet to bulk-import descriptors and rescan for historical transactions. New activity is picked up automatically without this.
- On subsequent wallet opens the server starts automatically (when RPC is configured).
The status label in the settings dialog reports connection state and the embedded server's address.
- Historical sync still needs the bulk import + rescan. On-demand
(1.7) watching imports scripts with
timestamp: now, so it only guarantees future activity; past transactions appear after the bulk-import rescan. A managed "rescan since date" that makes the xpub bulk import fully optional is planned. - History lookup is
O(mempool size)perget_historycall. Fine at personal-server scale; a per-address incremental index would scale better on a busy mainnet node. - Protocol ≤ 1.6 clients can only be served for the wallet's known addresses (scripthashes can't be reversed); arbitrary-address support requires a 1.7 client.
- One Electrum client at a time is the design assumption (multiple work, but there is no per-client deduplication of work).
- Lightning is not supported.
python3 -m unittest discover -s tests -vor, if you prefer pytest:
pip install pytest && pytest tests/Tests stub the electrum package, so you don't need an Electrum
checkout to run them.
MIT — see LICENSE.