Fulcrum - tulliolo/mobybolt GitHub Wiki

Fulcrum is a fast & nimble SPV server for Bitcoin created by Calin Culianu. It can be used as an alternative to Electrs because of its performance, as we can see in Craig Raw's comparison of servers.

❗ 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

❗ Fulcrum is a replacement for Electrs, these two services cannot be run at the same time (due to the same standard ports used). If you already have Electrs 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 fulcrum directory:

$ mkdir fulcrum

Prepare the environment

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

$ nano .env
# fulcrum
FULCRUM_VERSION=1.11.1
FULCRUM_ADDRESS=172.16.21.5
FULCRUM_GID=1101
FULCRUM_UID=1101

💡 In this file:

  1. we define the FULCRUM_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 fulcrum user.

Prepare the banner

Create the banner file with the following content (it will be shown when Fulcrum starts):

$ nano fulcrum/fulcrum-banner.txt
    ______      __                            
   / ____/_  __/ /___________  ______ ___     
  / /_  / / / / / ___/ ___/ / / / __ `__ \    
 / __/ / /_/ / / /__/ /  / /_/ / / / / / /    
/_/    \__,_/_/\___/_/   \__,_/_/ /_/ /_/     
    __  ___      __          ____        ____ 
   /  |/  /___  / /_  __  __/ __ )____  / / /_
  / /|_/ / __ \/ __ \/ / / / __  / __ \/ / __/
 / /  / / /_/ / /_/ / /_/ / /_/ / /_/ / / /_  
/_/  /_/\____/_.___/\__, /_____/\____/_/\__/  
                   /____/                     

Prepare the Dockerfile

Create the Dockerfile and populate it with the following content:

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

# install dependencies
RUN set -eux && \
    apt update && \
    apt install -y \
        build-essential \
        curl \
        git \
        libbz2-dev \
        libjemalloc-dev \
        libzmq3-dev \
        pkg-config \
        qt5-qmake \
        qtbase5-dev \
        zlib1g-dev && \
    rm -rf /var/lib/apt/lists/*


# builder image
FROM base AS builder

ARG FULCRUM_VERSION

ENV FULCRUM_URL="https://github.com/cculianu/Fulcrum.git"

RUN set -eux && \
    # download repository
    git clone --branch "v$FULCRUM_VERSION" $FULCRUM_URL

WORKDIR /Fulcrum

# install fulcrum
RUN set -eux && \
    # verify signatures
    git checkout "v$FULCRUM_VERSION" && \
    # configure
    qmake -makefile PREFIX=/usr/local Fulcrum.pro && \
    # build
    make -j$(nproc) && \
    # install
    make install


# result image
FROM debian:stable-slim

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

# install dependencies
RUN set -eux && \
    apt update && \
    apt install -y \
        libjemalloc-dev \
        libqt5network5 \
        libzmq3-dev \
        python3 && \
    rm -rf /var/lib/apt/lists/*

# default uid for fulcrum user
ARG FULCRUM_GID=1101
ARG FULCRUM_UID=1101

ARG BITCOIN_GID=1100

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

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

# switch user
USER fulcrum
COPY ./fulcrum-banner.txt /home/fulcrum/.fulcrum/

💡 In this file:

  1. we define a builder image (builder) to buid fulcrum from github sources, verifying version tag signatures;
  2. we define a result image:
    1. installing some needed dependencies;
    2. copying binaries from builder image;
    3. configuring the fulcrum user and the directories to which he will have access;
    4. adding the fulcrum user to the bitcoin group for RPC cookie authentication;
    5. 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 fulcrum/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/fulcrum/fulcrum.conf"

if [[ $# -eq 0 ]]; then
  # missing parameters, run fulcrum
  CMD="/usr/local/bin/Fulcrum $CONF_FILE"
fi

exec $CMD

💡 In this file:

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

Configure Fulcrum

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

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

## Bitcoin Core settings
bitcoind = bitcoin:8332
rpccookie = /data/bitcoin/.cookie

## Admin Script settings
admin = 8000

## Fulcrum server general settings
datadir = /home/fulcrum/db
pidfile = /run/fulcrum/fulcrum.pid
tcp = 0.0.0.0:50001
peering = false

# Set utxo-cache according to your device,
# recommended: utxo-cache=1/2 x RAM available e.g: 4GB RAM -> dbcache=2048
utxo-cache = 4096

# Banner
banner = /home/fulcrum/.fulcrum/fulcrum-banner.txt

Prepare the Docker Compose file

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

$ nano fulcrum/docker-compose.yml
services:
  electrumserver:
    build:
      context: .
      args:
        FULCRUM_VERSION: ${FULCRUM_VERSION}
        FULCRUM_GID: ${FULCRUM_GID}
        FULCRUM_UID: ${FULCRUM_UID}
    container_name: ${COMPOSE_PROJECT_NAME}_fulcrum
    depends_on:
      bitcoin:
        condition: service_healthy
    expose:
      - "50001:50001"
    image: ${COMPOSE_PROJECT_NAME}/fulcrum:${FULCRUM_VERSION}
    networks:
      default:
        ipv4_address: ${FULCRUM_ADDRESS}
    restart: unless-stopped
    stop_grace_period: 3m
    volumes:
      - fulcrum-data:/home/fulcrum/db/
      - bitcoin-data:/data/bitcoin/:ro
      - ./fulcrum.conf:/home/fulcrum/fulcrum.conf:ro

volumes:
  fulcrum-data:

💡 In this file:

  1. we build the Dockerfile and create an image named mobybolt/fulcrum:<version>;
  2. we define the restart policy of the container in case of failures;
  3. we declare the bitcoin service as a dependency (Fulcrum 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_fulcrum-data to store persistent data;
    4. with the FULCRUM_ADDRESS static address.

Link the Docker Compose File

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

$ nano docker-compose.yml

The file should look like this:

include:
  - ...
  - fulcrum/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 fulcrum image by typing:

$ docker compose build electrumserver

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

$ docker images | grep fulcrum
> mobybolt/fulcrum                    1.11.1               03c38d632c76   About a minute ago   345MB

Run

Run the following command and check the output:

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

Check the container logs:

$ docker logs mobybolt_fulcrum
> ...
> [2024-07-25 08:16:13.743] <Controller> Processed height: 1000, 0.1%, 88.4 blocks/sec, 90.1 txs/sec, 92.4 addrs/sec
> [2024-07-25 08:16:31.127] <Controller> Processed height: 2000, 0.2%, 57.5 blocks/sec, 58.2 txs/sec, 62.7 addrs/sec
> [2024-07-25 08:16:41.038] <Controller> Processed height: 3000, 0.4%, 100.9 blocks/sec, 102.9 txs/sec, 106.9 addrs/sec
> ...

Check the container status:

$ docker ps | grep fulcrum
> fbbd387b08d7   mobybolt/fulcrum:1.11.1                    "docker-entrypoint.sh"   48 minutes ago      Up 48 minutes                0/tcp                                                                                              mobybolt_fulcrum

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

$ docker volume ls | grep fulcrum
> local     mobybolt_fulcrum-data

Fulcrum is syncing

Fulcrum 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 Fulcrum is ready.

Fulcrum 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_fulcrum-data volume, follow the next steps:

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

When you see logs like this SrvMgr: starting 3 services ..., which means that Fulcrum is fully indexed

[2024-06-09 10:28:56.705] SrvMgr: starting 3 services ...
[2024-06-09 10:28:56.706] Starting listener service for TcpSrv 0.0.0.0:50001 ...
[2024-06-09 10:28:56.706] Service started, listening for connections on 0.0.0.0:50001
[2024-06-09 10:28:56.706] Starting listener service for SslSrv 0.0.0.0:50002 ...
[2024-06-09 10:28:56.706] Service started, listening for connections on 0.0.0.0:50002
[2024-06-09 10:28:56.707] Starting listener service for AdminSrv 127.0.0.1:8000 ...
[2024-06-09 10:28:56.707] Service started, listening for connections on 127.0.0.1:8000
[2024-06-09 10:28:56.707] <Controller> Starting ZMQ Notifier (hashblock) ...

Fulcrum Admin Script

Fulcrum comes with an admin script. The admin service, only available once Fulcrum is fully synchronized, is used for sending special control commands to the server. You may send commands to Fulcrum using this script.

Type the next command to see a list of possible subcommands that you can send to Fulcrum

$ docker exec mobybolt_fulcrum FulcrumAdmin -h
> usage: FulcrumAdmin [-h] -p port [-j] [-H [host]]
                  {addpeer,ban,banpeer,bitcoind_throttle...
> ...

Type the next command to get complete server information

$ docker exec mobybolt_fulcrum FulcrumAdmin -p 8000 getinfo

â„šī¸ Get more information about this command in the official documentation section

Remote access over SSL/TLS

As mentioned above, even though Fulcrum supports SSL/TLS connections, we'll still use nginx for these connections.

To perform this configuration:

  • 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" # electrumserver
    restart: unless-stopped

    💡 This configuration will allow the Fulcrum 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 Electrum 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 Fulcrum release page for a new version and change the FULCRUM_VERSION value in the .env file. Then, redo the steps described in:

  1. Build
  2. Run

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

$ docker images | grep fulcrum
> mobybolt/fulcrum     1.11.1   03c38d632c76   3 minutes ago    345MB
> mobybolt/fulcrum     1.10.0   3613ae3d3613   14 minutes ago   322MB
$ docker image rm mobybolt/fulcrum:1.10.0
> Untagged: mobybolt/fulcrum:0.4.8.10
> 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 fulcrum.

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" # electrumserver in the ports section (or remove the entire ports section, if fulcrum is the only entry)

  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 Fulcrum 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_fulcrum  Removed
> ...

Unlink fulcrum

  1. Edit the main docker-compose file (nano docker-compose.yaml) and remove the Fulcrum 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 fulcrum | awk '{print $3}')
> Untagged: mobybolt/fulcrum:1.11.1
> 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_fulcrum-data
> mobybolt_fulcrum-data

Remove files and directories (optional)

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