Electrs - tulliolo/mobybolt GitHub Wiki

Electrs is an efficient re-implementation of Electrum Server in Rust, inspired by ElectrumX, Electrum Personal Server and bitcoincore-indexd.

❗ To follow this section, log in to your node as admin user via Secure Shell (SSH) and access the project's home:

$ cd apps/mobybolt

Table of contents

Requirements

❗ Electrs is a replacement for Fulcrum, these two services cannot be run at the same time (due to the same standard ports used). If you already have Fulcrum installed, please uninstall it before proceeding.

Introduction

Bitcoin with hardware wallets

The best way to safely keep your bitcoin (meaning the best combination of security and usability) is to use a hardware wallet (like BitBox, Coldcard, Ledger, or Trezor) in combination with your own Bitcoin node. This gives you security, privacy and eliminates the need to trust a third party to verify transactions.

Bitcoin Knots/Core on the MobyBolt itself is not meant to hold funds.

One possibility to use Bitcoin Core with your Bitcoin wallets is to use an Electrum server as middleware. It imports data from Bitcoin Knots/Core and provides it to software wallets supporting the Electrum protocol. Desktop wallets like Sparrow, the BitBoxApp, Electrum, or Specter Desktop that support hardware wallets can then be used with your own sovereign Bitcoin node.

Prepare

Create the electrs directory:

$ mkdir electrs

Prepare the environment

Edit the .env file and append the following content to the end:

$ nano .env
# electrs
ELECTRS_VERSION=0.10.6
ELECTRS_ADDRESS=172.16.21.5
ELECTRS_GID=1101
ELECTRS_UID=1101

💡 In this file:

  1. we define the ELECTRS_VERSION (check the latest available version here);
  2. we define a static address for the container;
  3. we define the uid (user id) and gid (group id) of the electrs user.

Prepare the Dockerfile

Create the Dockerfile and populate it with the following content:

$ nano electrs/Dockerfile
# base image
FROM debian:stable-slim AS base

# install dependencies
RUN set -eux && \
    apt update && \
    apt install -y \
        cargo \
        clang \
        cmake \
        curl \
        git \
        gpg && \
    rm -rf /var/lib/apt/lists/*


# builder image
FROM base AS builder

ARG ELECTRS_VERSION

ENV ELECTRS_URL="https://github.com/romanz/electrs.git" \
    ELECTRS_SIG_URL="https://romanzey.de/pgp.txt"

# clone repository
RUN set -eux && \
    git clone --branch "v$ELECTRS_VERSION" $ELECTRS_URL

WORKDIR electrs

# build electrs
RUN set -eux && \
    # import signatures
    curl $ELECTRS_SIG_URL | gpg --import && \
    # verify signatures
    git verify-tag "v$ELECTRS_VERSION" && \
    # build
    cargo build --locked --release && \
    # install
    install -m 0755 -o root -g root -t /usr/local/bin ./target/release/electrs


# result image
FROM debian:stable-slim

COPY --from=builder /usr/local/ /usr/local/

# default guid for electrs user
ARG ELECTRS_GID=1101
ARG ELECTRS_UID=1101

ARG BITCOIN_GID=1100

RUN set -xe && \
    # create electrs user
    addgroup --gid $ELECTRS_GID electrs && \
    adduser --disabled-password --comment "" --gid $ELECTRS_GID --uid $ELECTRS_UID electrs && \
    # create bitcoin group and add electrs user to bitcoin group
    addgroup --gid $BITCOIN_GID bitcoin && \
    adduser electrs bitcoin && \
    # setup dirs and permissions
    mkdir -p /home/electrs/db/ && \
    chmod 0700 /home/electrs/db/ && \
    chown -R electrs:electrs /home/electrs/db/

# setup entrypoint
COPY --chmod=0755 docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

# switch user
USER electrs

💡 In this file:

  1. we define a builder image (builder) to buid electrs from github sources, verifying version tag signatures;
  2. we define a result image:
    1. copying binaries from builder image;
    2. configuring the electrs user and the directories to which he will have access;
    3. adding the electrs user to the bitcoin group for RPC cookie authentication;
    4. setting the entrypoint (the script to run when the container starts).

Create the entrypoint

Create the entrypoint file and populate it with the following content:

$ nano electrs/docker-entrypoint.sh
#!/bin/bash
set -eo pipefail

function die_func() {
  echo "INFO: got SIGTERM... exiting"
  exit 1
}
trap die_func TERM

CMD=$@

CONF_FILE=/home/electrs/electrs.conf

if [[ $# -eq 0 ]]; then
  # missing parameters, run electrs
  CMD="/usr/local/bin/electrs --conf $CONF_FILE --skip-default-conf-files"
fi

exec $CMD

💡 In this file:

  1. we define the configuration file;
  2. we run electrs.

Configure Electrs

Create the electrs configuration file and populate it with the following content:

$ nano electrs/electrs.conf
# MobyBolt: electrs configuration
# /home/electrs/electrs.conf

# Bitcoin Core settings
network = "bitcoin"
daemon_dir = "/data/bitcoin"
daemon_rpc_addr = "bitcoin:8332"
daemon_p2p_addr = "bitcoin:8333"

# Electrs settings
electrum_rpc_addr = "0.0.0.0:50001"
db_dir = "/home/electrs/db"
server_banner = "Welcome to electrs (Electrum Rust Server) running on a MobyBolt node!"
skip_block_download_wait = true

# Logging
log_filters = "INFO"

Prepare the Docker Compose file

Create the Docker Compose file and populate it with the following content:

$ nano electrs/docker-compose.yml
services:
  electrumserver:
    build:
      context: .
      args:
        ELECTRS_VERSION: ${ELECTRS_VERSION}
        ELECTRS_GID: ${ELECTRS_GID}
        ELECTRS_UID: ${ELECTRS_UID}
    container_name: ${COMPOSE_PROJECT_NAME}_electrs
    depends_on:
      bitcoin:
        condition: service_healthy
    expose:
      - "50001:50001"
    image: ${COMPOSE_PROJECT_NAME}/electrs:${ELECTRS_VERSION}
    networks:
      default:
        ipv4_address: ${ELECTRS_ADDRESS}
    restart: unless-stopped
    stop_grace_period: 3m
    volumes:
      - electrs-data:/home/electrs/db/
      - bitcoin-data:/data/bitcoin/:ro
      - ./electrs.conf:/home/electrs/electrs.conf:ro

volumes:
  electrs-data:

💡 In this file:

  1. we build the Dockerfile and create an image named mobybolt/electrs:<version>;
  2. we define the restart policy of the container in case of failures;
  3. we declare the bitcoin service as a dependency (Electrs will not run if bitcoin is not active);
  4. we provide the container:
    1. with the previously defined configuration (bind mount);
    2. with the bitcoin volume data, from which it will reach the RPC authentication cookie;
    3. with a volume named mobybolt_electrs-data to store persistent data;
    4. with the ELECTRS_ADDRESS static address.

Link the Docker Compose File

Edit the main Docker Compose file and link the electrs docker compose file in the include section:

$ nano docker-compose.yml

The file should look like this:

include:
  - ...
  - electrs/docker-compose.yml

âš ī¸ Be very careful to respect the indentation. Yaml is very sensitive to this! âš ī¸

Test the Docker Compose File

Run the following command and check the output:

$ docker compose config --quiet && printf "OK\n" || printf "ERROR\n"
> OK

💡 If the output is not OK, check the error reported... Maybe some wrong indentation in the yaml files?

Build

Let's build the electrs image by typing:

$ docker compose build electrumserver

Check for a new image called mobybolt/electrs:<version>:

$ docker images | grep electrs
> mobybolt/electrs                    0.10.6               5389bd930062   37 seconds ago   102MB

Test

Run the following command and check the output:

$ docker run --rm --network mobybolt_default -v mobybolt_bitcoin-data:/data/bitcoin/:ro -v ./electrs/electrs.conf:/home/electrs/electrs.conf:ro mobybolt/electrs:0.10.6
> Starting electrs 0.10.6 on x86_64 linux with Config { network: Bitcoin, db_path: "/home/electrs/db/bitcoin", db_log_dir: None, daemon_dir: "/data/bitcoin", daemon_auth: CookieFile("/data/bitcoin/.cookie"), daemon_rpc_addr: 172.16.21.4:8332, daemon_p2p_addr: 172.16.21.4:8333, electrum_rpc_addr: 0.0.0.0:50001, monitoring_addr: 127.0.0.1:4224, wait_duration: 10s, jsonrpc_timeout: 15s, index_batch_size: 10, index_lookup_limit: None, reindex_last_blocks: 0, auto_reindex: true, ignore_mempool: false, sync_once: false, skip_block_download_wait: true, disable_electrum_rpc: false, server_banner: "Welcome to electrs (Electrum Rust Server) running on a MobyBolt node!", signet_magic: f9beb4d9 }
> [2024-10-04T08:55:46.391Z INFO  electrs::metrics::metrics_impl] serving Prometheus metrics on 127.0.0.1:4224
> [2024-10-04T08:55:46.391Z INFO  electrs::server] serving Electrum RPC on 0.0.0.0:50001
> [2024-10-04T08:55:46.543Z INFO  electrs::db] "/home/electrs/db/bitcoin": 0 SST files, 0 GB, 0 Grows
> [2024-10-04T08:55:46.596Z INFO  electrs::index] indexing 2000 blocks: [1..2000]
> [2024-10-04T08:55:46.800Z INFO  electrs::chain] chain updated: tip=00000000dfd5d65c9d8561b4b8f60a63018fe3933ecb131fb37f905f87da951a, height=2000
> ...

Press CTRL-C to exit.

Run

Run the following command and check the output:

$ docker compose up -d electrumserver
> [+] Running 2/2
> ✔ Volume "mobybolt_electrs-data"  Created
> ✔ Container mobybolt_electrs      Started

Check the container logs:

$ docker logs mobybolt_electrs
> mobybolt/electrs:0.10.6
> Starting electrs 0.10.6 on x86_64 linux with Config { network: Bitcoin, db_path: "/home/electrs/db/bitcoin", db_log_dir: None, daemon_dir: "/data/bitcoin", daemon_auth: CookieFile("/data/bitcoin/.cookie"), daemon_rpc_addr: 172.16.21.4:8332, daemon_p2p_addr: 172.16.21.4:8333, electrum_rpc_addr: 0.0.0.0:50001, monitoring_addr: 127.0.0.1:4224, wait_duration: 10s, jsonrpc_timeout: 15s, index_batch_size: 10, index_lookup_limit: None, reindex_last_blocks: 0, auto_reindex: true, ignore_mempool: false, sync_once: false, skip_block_download_wait: true, disable_electrum_rpc: false, server_banner: "Welcome to electrs (Electrum Rust Server) running on a MobyBolt node!", signet_magic: f9beb4d9, args: [] }
> [2024-09-27T09:01:14.688Z INFO  electrs::metrics::metrics_impl] serving Prometheus metrics on 127.0.0.1:4224
> [2024-09-27T09:01:14.688Z INFO  electrs::server] serving Electrum RPC on 0.0.0.0:50001
> [2024-09-27T09:01:14.808Z INFO  electrs::db] "/home/electrs/db/bitcoin": 0 SST files, 0 GB, 0 Grows
> [2024-09-27T09:01:14.916Z INFO  electrs::index] indexing 2000 blocks: [1..2000]
> [2024-09-27T09:01:15.282Z INFO  electrs::chain] chain updated: tip=00000000dfd5d65c9d8561b4b8f60a63018fe3933ecb131fb37f905f87da951a, height=2000
> ...

Check the container status:

$ docker ps | grep electrs
> fbbd387b08d7   mobybolt/electrs:0.10.6                    "docker-entrypoint.sh"   48 minutes ago      Up 48 minutes                0/tcp                                                                                              mobybolt_electrs

â„šī¸ If not already present, docker will also create the mobybolt_electrs-data volume. You can check for it with the command:

$ docker volume ls | grep electrs
> local     mobybolt_electrs-data

Electrs is syncing

Electrs must first fully index the blockchain and compact its database before you can connect to it with your wallets. This can take up to ~1.5 - 4 days or more, depending on the hardware. Only proceed with the Blockchain explorer: BTC RPC Explorer and Desktop Wallet Section once Electrs is ready.

Electrs will now index the whole Bitcoin blockchain so that it can provide all necessary information to wallets. With this, the wallets you use no longer need to connect to any third-party server to communicate with the Bitcoin peer-to-peer network.

❗ DO NOT REBOOT OR STOP THE SERVICE DURING THE DB CREATION PROCESS. YOU MAY CORRUPT THE FILES - in case that happens, start the sync from scratch by deleting the mobybolt_electrs-data volume, follow the next steps:

  • Remove Electrs container:
    $ docker compose down electrumserver
    > [+] Running 2/1
    > ✔ Container mobybolt_electrs  Removed
    
  • Remove Electrs volume:
    $ docker volume rm mobybolt_electrs-data
    > mobybolt_electrs-data
    
  • Follow the run section again.

Remote access over SSL/TLS

To enable nginx reverse proxy to add SSL/TLS encryption to the Electrs communication, follow the next steps:

  • create the electrumserver-reverse-proxy configuration file with the following contents:

    $ nano nginx/config/streams-enabled/electrumserver-reverse-proxy.conf
    
    upstream electrumserver {
      server 172.16.21.5:50001;
    }
    server {
      listen 50002 ssl;
      proxy_pass electrumserver;
    }
  • test nginx configuration:

    $ docker exec mobybolt_nginx nginx -t
    > nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    > nginx: configuration file /etc/nginx/nginx.conf test is successful
    
  • edit the nginx docker-compose file adding a ports section between image and restart; the file should look like below:

    $ nano nginx/docker-compose.yml
    
    image: ${COMPOSE_PROJECT_NAME}/nginx:${NGINX_VERSION}
    ports:
      - "50002:50002" # electrs
    restart: unless-stopped

    💡 This configuration will allow the Electrs ssl/tls port in the docker-managed firewall.

    âš ī¸ Be very careful to respect the indentation. Yaml is very sensitive to this! âš ī¸

  • test the docker compose file:

    $ docker compose config --quiet && printf "OK\n" || printf "ERROR\n"
    > OK
    
  • recreate the nginx container:

    $ docker compose down nginx && docker compose up -d nginx
    > [+] Running 2/1
    >  ✔ Container mobybolt_nginx  Removed                                                                                                0.8s 
    >  ! Network mobybolt_default  Resource is still in use                                                                               0.0s 
    > [+] Running 1/1
    >  ✔ Container mobybolt_nginx  Started
    
  • check the nginx container status (it should be healthy, if not repeat the command):

    $ docker ps | grep nginx
    > bc38795edbb3   mobybolt/nginx:mainline                    "/docker-entrypoint.â€Ļ"   2 hours ago    Up 5 minutes (healthy)   0.0.0.0:4000->4000/tcp, :::4000->4000/tcp, 80/tcp, 0.0.0.0:50002->50002/tcp, :::50002->50002/tcp   mobybolt_nginx
    

Remote access over Tor

  • Edit the tor configuration file, adding the following content at the end:

    $ nano tor/torrc
    
    ## Hidden Service Electum Server
    HiddenServiceDir /var/lib/tor/hidden_service_electrumserver/
    HiddenServiceVersion 3
    HiddenServicePoWDefensesEnabled 1
    HiddenServicePort 50001 172.16.21.5:50001
  • Check the tor configuration file:

    $ docker exec mobybolt_tor tor -f /etc/tor/torrc --verify-config
    > ...
    > Configuration was valid
    
  • Restart tor:

    $ docker compose restart tor
    > [+] Restarting 1/1
    >  ✔ Container mobybolt_tor  Started
    
  • check the tor container status (it should be healthy, if not repeat the command):

    $ docker ps | grep tor
    > bfc225986e0d   mobybolt/tor:0.4.8.12                      "docker-entrypoint.sh"   3 hours ago      Up About a minute (healthy)   9050-9051/tcp
    
  • get your onion address:

    $ docker exec mobybolt_tor cat /var/lib/tor/hidden_service_electrumserver/hostname
    > abcdefg..............xyz.onion
    

Upgrade

Check the Electrs release page for a new version and change the ELECTRS_VERSION value in the .env file. Then, redo the steps described in:

  1. Build
  2. Test
  3. Run

If everything is ok, you can clear the old image and build cache, like in the following example:

$ docker images | grep electrs
> mobybolt/electrs     0.10.6   03c38d632c76   3 minutes ago    345MB
> mobybolt/electrs     0.10.5   3613ae3d3613   14 minutes ago   322MB
$ docker image rm mobybolt/electrs:0.10.5
> Untagged: mobybolt/electrs:0.10.5
> Deleted: sha256:3613ae3d36137e9e4dd38e93d40edd21b8e4aa17df5527e934aed2013087537a
$ docker buildx prune
> WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y
> ID                                              RECLAIMABLE     SIZE            LAST ACCESSED
> pbzeixdrvu87hv3rajkrfprr8                       true            398B            24 minutes ago
> xdufppotcvx2kegu5gc3zscg6*                      true            621.8MB         17 minutes ago
> ...
> Total:  1.853GB

Uninstall

Follow the next steps to uninstall electrs.

Unlink nginx

  1. Remove the electrumserver-reverse-proxy configuration file:

    $ rm nginx/config/streams-enabled/electrumserver-reverse-proxy.conf
    
  2. Edit the nginx docker-compose file and remove the line - "50002:50002" # electrs in the ports section

  3. Test the docker-compose file:

    $ docker compose config --quiet && printf "OK\n" || printf "ERROR\n"
    > OK
    
  4. Recreate the nginx container:

    $ docker compose down nginx && docker compose up -d nginx
    > [+] Running 2/1
    >  ✔ Container mobybolt_nginx  Removed                                                                                                0.8s 
    >  ! Network mobybolt_default  Resource is still in use                                                                               0.0s 
    > [+] Running 1/1
    >  ✔ Container mobybolt_nginx  Started
    
  5. Check the nginx container status (it should be healthy, if not repeat the command):

    $ docker ps | grep nginx
    > bc38795edbb3   mobybolt/nginx:mainline                    "/docker-entrypoint.â€Ļ"   2 hours ago    Up 5 minutes (healthy)   0.0.0.0:4000->4000/tcp, :::4000->4000/tcp, 80/tcp, 0.0.0.0:50002->50002/tcp, :::50002->50002/tcp   mobybolt_nginx
    

Unlink tor

  1. Edit the tor configuration file (nano tor/torrc) and remove all the Electrs lines added above

  2. Check the tor configuration file:

    $ docker exec mobybolt_tor tor -f /etc/tor/torrc --verify-config
    > ...
    > Configuration was valid
    
  3. Restart tor:

    $ docker compose restart tor
    > [+] Restarting 1/1
    >  ✔ Container mobybolt_tor  Started
    
  4. Check the tor container status (it should be healthy, if not repeat the command):

    $ docker ps | grep tor
    > bfc225986e0d   mobybolt/tor:0.4.8.12                      "docker-entrypoint.sh"   3 hours ago      Up About a minute (healthy)   9050-9051/tcp
    

Remove the container

$ docker compose down electrumserver
> [+] Running 2/1
> ✔ Container mobybolt_electrs  Removed
> ...

Unlink Electrs

  1. Edit the main docker-compose file (nano docker-compose.yaml) and remove the Electrs line added above

  2. Test the docker-compose file:

    $ docker compose config --quiet && printf "OK\n" || printf "ERROR\n"
    > OK
    

Remove the image

$ docker image rm $(docker images | grep electrs | awk '{print $3}')
> Untagged: mobybolt/electrs:0.10.6
> Deleted: sha256:13afebf08e29c6b9a526a6e54ab1f93e745b25080add4e37af8f08bdf6cfbcc6

Clean the build cache

$ docker buildx prune
> WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y
> ID                                              RECLAIMABLE     SIZE            LAST ACCESSED
> 7r8ccrpq0g0e03deu2dh53ob6*                      true            9.69MB          19 minutes ago
> ndrhcdo756vejnx17qm775t08*                      true            1.212kB         24 minutes ago
> ...

Remove the volume (optional)

$ docker volume rm mobybolt_electrs-data
> mobybolt_electrs-data

Remove files and directories (optional)

$ rm -rf electrs
âš ī¸ **GitHub.com Fallback** âš ī¸