From 3e5d005efe8c4085f310413c59d6be0502c5054a Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Thu, 22 Oct 2026 15:15:13 +0200 Subject: [PATCH] Add DEPLOY.md: ports, firewall, RPC credentials, SSL, operation Also fix the COIN default in docs/environment.rst, and refer to the Palladium node setup by concept instead of by paths of its repository. Co-Authored-By: Claude Sonnet 5.5 --- CLAUDE.md | 2 +- DEPLOY.md | 162 ++++++++++++++++++++++++++++++++++++ Dockerfile | 2 +- README.md | 13 ++- docker-compose.yml | 2 +- docs/environment.rst | 2 +- tests/lib/test_addresses.py | 2 +- tests/lib/test_palladium.py | 2 +- 8 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 DEPLOY.md diff --git a/CLAUDE.md b/CLAUDE.md index 55ef075..a7a7ab3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,7 +22,7 @@ DB_ENGINE=rocksdb DB_DIRECTORY=... DAEMON_URL=http://user:pass@host:port/ electr electrumx_rpc getinfo # talk to a running server via local RPC ``` -Docker: `docker compose up -d --build` (root `Dockerfile`, `docker-compose.yml`, `.env` from `.env.example`) runs the server on the external `palladium` network of the node in palladiumcore/docker, daemon at `palladiumd:2332`. +Docker: `docker compose up -d --build` (root `Dockerfile`, `docker-compose.yml`, `.env` from `.env.example`) runs the server on the external `palladium` network of the Palladium node container, daemon at `palladiumd:2332`. Configuration is entirely via environment variables, parsed in `src/electrumx/server/env.py` (documented in `docs/environment.rst`). `DB_ENGINE` is mandatory in 2.0 (`leveldb` or `rocksdb`). Python >=3.10; free-threaded 3.14t is experimental. diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..333790e --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,162 @@ +# Deploying plm-electrumx + +What to set up to run a Palladium Electrum server with Docker, next to the Palladium node +running in Docker (service `palladiumd` on the Docker network `palladium`). +Quick start is in [README.md](README.md); this file covers ports, security and operation. + +## Overview + +``` +Electrum clients ──50001 (tcp) / 50002 (ssl)──▶ electrumx ──palladiumd:2332 (RPC)──▶ palladiumd ◀── 2333 ──▶ Palladium network + container private "palladium" network container +``` + +The node and ElectrumX talk over the private Docker network `palladium`. The only things that must be +reachable from outside are the node's P2P port and the Electrum port(s) of this server. + +## Node requirements + +In `~/.palladium/palladium.conf` (restart the node container after a change): + +```ini +server=1 +txindex=1 # required: ElectrumX looks up any transaction by id +# prune must NOT be set: ElectrumX reads the whole chain +``` + +Start the node **before** ElectrumX: it creates the `palladium` network, which `docker-compose.yml` declares +as `external`. If the network is missing, `docker compose up` fails with `network palladium declared as external, +but could not be found`. + +## RPC credentials + +ElectrumX logs in to the node with `PLM_RPC_USER` / `PLM_RPC_PASSWORD` from `.env`. The defaults written by the node +(`rpcuser=username`) are only fine to try it. For a real server create a user of its own with +the `rpcauth.py` helper that comes with Bitcoin Core-based nodes (Palladium Core included): + +```bash +python3 rpcauth.py electrumx # prints the rpcauth= line and the password (shown once) +``` + +Put the `rpcauth=...` line in `palladium.conf` (and remove `rpcuser`/`rpcpassword`), restart the node, and put the +user and password in `.env`. Anything on the `palladium` network can try to log in, so use a long password. +Keep `.env` private (`chmod 600 .env`); it is ignored by git. + +## Ports + +| Port | What | Open to | Notes | +| --- | --- | --- | --- | +| **50001/tcp** | Electrum protocol, plain | clients (internet, if the server is public) | published by `docker-compose.yml` | +| **50002/tcp** | Electrum protocol over SSL | clients | optional, needs a certificate (see below); not published by default | +| **2333/tcp** | Palladium P2P (the node) | the internet, inbound | published by the node's compose file | +| 2332/tcp | node RPC | **only the `palladium` network** | never publish or forward it: it can spend wallet funds | +| 28332, 28334, 28335/tcp | node ZMQ | only the `palladium` network | never publish or forward them | + +Changing the published port: `ELECTRUMX_TCP_PORT` in `.env` (host side only; the container always listens on 50001). + +A server only for yourself (or for other containers) needs no open port: connect to `127.0.0.1:50001`, or join the +`palladium` network and use `electrumx:50001`. + +### Firewall + +- Docker publishes `ports:` by writing its own firewall rules, which **bypass `ufw` and `firewalld`**. A published + port is open to the internet even if `ufw` denies it. To keep a port private, bind it to an address, for example + `"127.0.0.1:50001:50001"` in `docker-compose.yml`. +- For a public server, allow what you want reachable: `sudo ufw allow 2333/tcp` (node) and the Electrum port(s). + Since Docker bypasses ufw for published ports, this matters mostly for services not run by Docker. + +### Router (server behind NAT) + +Give the server a fixed LAN address (DHCP reservation) and forward to it: + +| External port | Protocol | To | Needed for | +| --- | --- | --- | --- | +| 2333 | TCP | `:2333` | the node (optional but recommended: more peers) | +| 50001 | TCP | `:50001` | a public Electrum server | +| 50002 | TCP | `:50002` | a public Electrum server with SSL | + +Never forward 2332 or 28332/28334/28335. A server on a VPS with a public IP opens the ports in the provider's firewall instead. + +## SSL (port 50002) + +Most Electrum wallets prefer SSL. You need a certificate and key for the host name of the server. + +1. Get a certificate, for example with Let's Encrypt (`certbot certonly --standalone -d electrum.example.com`), + or a self-signed one for tests: + ```bash + openssl req -x509 -newkey rsa:4096 -nodes -days 3650 -subj "/CN=electrum.example.com" \ + -keyout privkey.pem -out fullchain.pem + ``` +2. Create `docker-compose.override.yml` next to `docker-compose.yml` (compose reads it automatically): + ```yaml + services: + electrumx: + environment: + SERVICES: tcp://:50001,ssl://:50002 + SSL_CERTFILE: /certs/fullchain.pem + SSL_KEYFILE: /certs/privkey.pem + ports: + - "50002:50002" + volumes: + - /path/to/certs:/certs:ro + ``` + The container runs as uid 1000: the key must be readable by it (`chown 1000 privkey.pem; chmod 600 privkey.pem`). + With Let's Encrypt, copy the files out of `/etc/letsencrypt/live/...` (they are symlinks into `archive/`), and + copy them again after each renewal. +3. `docker compose up -d`, and open 50002 as in "Ports". + +## Announcing a public server + +`REPORT_SERVICES` in `.env` is the address other servers and clients are told to use. Set it only for a server that is +reachable from outside, with its public host name (not an IP on a private network, not `localhost`): + +```ini +REPORT_SERVICES=tcp://electrum.example.com:50001,ssl://electrum.example.com:50002 +``` + +Peer discovery is on by default (`PEER_DISCOVERY`, `PEER_ANNOUNCE`). `lib/coins.py` has no known Palladium servers yet +(`PEERS = []` in the `Palladium` class), so a new server has nobody to learn the network from. Add the +servers you know there (format: `'host s t'`, for example `'electrum.example.com s50002 t50001'`) and rebuild, or +keep the server private with `PEER_DISCOVERY=off` / `PEER_DISCOVERY=self`. + +## Resources and first start + +- On the first start ElectrumX builds its database from the node, from genesis. With the chain at about 542,000 blocks + it took roughly a minute on a development machine, ending at about 230 MB of database and 0.5 GB of RAM. + The database grows with the chain. +- `CACHE_MB` in `.env` is the memory used for caching during the sync (default in `docker-compose.yml`: 1200). +- The database is the `electrumx-db` volume. It is derived from the node, so it can be rebuilt at any time: + `docker compose down -v` deletes it and the next start syncs again. A backup is optional. +- Disk: the node needs `txindex`, so budget for the node's own data too (see the node's README). + +## Operation + +```bash +docker compose up -d --build # start / update after pulling changes +docker compose logs -f electrumx # follow the log +docker compose ps +docker compose stop # clean shutdown (up to 2 minutes to flush the db) +``` + +Is it up and synced? The log should end with `synced to height ` and the same height as the node +(`docker compose exec palladiumd palladium-cli getblockcount` in the node's compose project). +From the host, a protocol check: + +```bash +printf '{"id":1,"method":"server.version","params":["test","1.4"]}\n' | nc -q1 127.0.0.1 50001 +``` + +From another machine, `nc -zv 50001` must succeed if the server is meant to be public, and +`nc -zv 2332` must fail. + +## Troubleshooting + +| Symptom | Cause | +| --- | --- | +| `network palladium declared as external, but could not be found` | the node is not running (or its compose file was not started yet) | +| log: connection refused / cannot connect to `palladiumd:2332` | the node is stopped or still starting; check that the node container is running | +| log: `401`/authorization errors | wrong `PLM_RPC_USER` / `PLM_RPC_PASSWORD` in `.env`; the credentials must be the ones in the node's `palladium.conf` | +| errors mentioning `txindex` | `txindex=1` is missing in `palladium.conf` (an index added later is built while the node runs, which takes time) | +| `blockchain.outpoint.subscribe` or package broadcast return a daemon error | expected: Palladium Core is based on Bitcoin Core 0.19 and does not have these RPCs | +| the server stays at an old height | the node is not synced; ElectrumX only follows the node | +| clients cannot connect from outside | port not published/forwarded, firewall, or `SERVICES` does not list that protocol | diff --git a/Dockerfile b/Dockerfile index 1a78126..6ca3860 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # # docker compose up -d --build (see docker-compose.yml and README.md) # -# Runs on the "palladium" network of the node in palladiumcore/docker and reaches it as palladiumd:2332. +# Runs on the "palladium" network of the Palladium node container and reaches it as palladiumd:2332. FROM python:3.14-trixie AS builder diff --git a/README.md b/README.md index c9a8355..6ac371e 100644 --- a/README.md +++ b/README.md @@ -8,20 +8,19 @@ Language: Python (>= 3.10) ``` `plm-electrumx` is an [Electrum](https://electrum.org) protocol server for Palladium (PLM), the coin of -[palladiumcore](https://github.com/palladium-coin/palladiumcore). It is a fork of +Palladium Core. It is a fork of [spesmilo/electrumx](https://github.com/spesmilo/electrumx) that supports Palladium only. It connects to your Palladium full node and indexes the blockchain, allowing efficient querying of the history of arbitrary addresses. ## Running -### With Docker (next to the node of palladiumcore) +### With Docker (next to the Palladium node) -ElectrumX joins the `palladium` network created by the node in -[palladiumcore/docker](https://github.com/palladium-coin/palladiumcore/tree/docker-node/docker) and reaches it +ElectrumX joins the Docker network `palladium`, created by the node's compose project, and reaches the node as `palladiumd:2332`. RPC and ZMQ of the node are not published on the host, so nothing else is exposed. -1. Start the node (`docker compose up -d` in `palladiumcore/docker`). Its `~/.palladium/palladium.conf` needs +1. Start the node (a compose service named `palladiumd` on the network `palladium`). Its `~/.palladium/palladium.conf` needs `server=1` and `txindex=1` (no `prune`). 2. `cp .env.example .env` and set `PLM_RPC_USER` / `PLM_RPC_PASSWORD` to the RPC credentials of that file (use a dedicated `rpcauth` user for anything public). @@ -31,7 +30,7 @@ The first start builds the database from the node from genesis, which takes a wh `docker compose logs -f electrumx`. The database is the `electrumx-db` volume. Electrum clients connect to TCP port 50001 (`ELECTRUMX_TCP_PORT` in `.env`). To announce a public server to the network, set `REPORT_SERVICES` in `.env`. Settings are in `.env` and `docker-compose.yml`; the image defaults are in the -`Dockerfile`. +`Dockerfile`. Ports, firewall, SSL, RPC credentials and troubleshooting are in [DEPLOY.md](DEPLOY.md). ### Without Docker @@ -87,7 +86,7 @@ When merging upstream, resolve modify/delete conflicts on these files by keeping error to clients. - The daemon must run with `txindex=1`. - Same genesis blocks as Bitcoin (mainnet, testnet3, regtest), 80-byte headers, sha256d PoW, segwit. -- Parameters were taken from `src/chainparams.cpp` and `src/chainparamsbase.cpp` of palladiumcore. +- Parameters (address prefixes, ports, genesis hashes) were taken from the Palladium Core chain parameters. ## License diff --git a/docker-compose.yml b/docker-compose.yml index 572af14..5b76564 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -# ElectrumX for Palladium, next to the node of palladiumcore/docker. +# ElectrumX for Palladium, next to the Palladium node container. # Start the node first: it creates the "palladium" network. Setup and usage: README.md services: electrumx: diff --git a/docs/environment.rst b/docs/environment.rst index 8217fc6..7f9ca95 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -192,7 +192,7 @@ These environment variables are optional: .. envvar:: COIN Must be a :attr:`NAME` from one of the :class:`Coin` classes in - `lib/coins.py`_. Defaults to ``Bitcoin``. + `lib/coins.py`_. Defaults to ``Palladium``. .. envvar:: NET diff --git a/tests/lib/test_addresses.py b/tests/lib/test_addresses.py index be8c4b4..e85dcb2 100644 --- a/tests/lib/test_addresses.py +++ b/tests/lib/test_addresses.py @@ -29,7 +29,7 @@ import pytest import electrumx.lib.coins as coins addresses = [ - # addresses and scripts from palladiumcore src/test/data/key_io_valid.json + # valid addresses and their scripts, from the Palladium Core test vectors (coins.Palladium, "PHrYiyUQTSekTWvVBNMEnSzV1DCB9TGR2P", "65a16059864a2fdbc7c99a4723a8395bc6f188eb", "0fae27c959f6b69f711a9e"), (coins.Palladium, "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", diff --git a/tests/lib/test_palladium.py b/tests/lib/test_palladium.py index d2dd11b..dad6d96 100644 --- a/tests/lib/test_palladium.py +++ b/tests/lib/test_palladium.py @@ -1,4 +1,4 @@ -# Tests of the Palladium coin classes (parameters from palladiumcore src/chainparams.cpp) +# Tests of the Palladium coin classes (parameters of the Palladium Core chain params) import pytest