├── CHANGELOG.md ├── Dockerfile ├── README.md ├── VERSION ├── bin ├── append-to-hosts.sh ├── build-docker-bitcoin ├── get-bitcoin └── get-bitcoin.sh └── entrypoint.sh /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1 (Bitcoin 0.16.1) 2 | 3 | - Moved from an Ubuntu image (~4GB) to Alpine (~90MB). 4 | 5 | # 1.0 6 | 7 | - Big rewrite; pretty much everything has changed. On Debian now. 8 | - Volume mounts 9 | - Old: /root/.bitcoin 10 | - New: /bitcoin/data 11 | - Specify user config file by mounting volume to /bitcoin/bitcoin.conf 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM buildpack-deps:buster-curl as builder 2 | LABEL MAINTAINER="James O'Beirne " 3 | 4 | # These buildargs can be set during container build time with e.g. 5 | # --build-arg VERSION=[version] 6 | 7 | # Can be "release" or "git" 8 | ARG SOURCE= 9 | ENV PYTHONUNBUFFERED=1 10 | 11 | RUN apt-get update && \ 12 | apt-get install -y gnupg2 curl sudo python3 && \ 13 | rm -rf /var/lib/apt/lists/* 14 | 15 | # Install build deps, if necessary. 16 | RUN if [ "${SOURCE}" = "git" ] ; then apt-get update && \ 17 | apt-get install -y \ 18 | git build-essential libtool autotools-dev automake binutils \ 19 | pkg-config bsdmainutils libevent-dev libboost-dev libsqlite3-dev \ 20 | systemtap-sdt-dev libzmq3-dev g++ && \ 21 | rm -rf /var/lib/apt/lists/* ; fi 22 | 23 | ARG VERSION= 24 | ARG GIT_REF= 25 | ARG GIT_SHA= 26 | ARG GIT_REPO_URL= 27 | ARG CONFIGURE_ARGS= 28 | 29 | LABEL "bitcoin-source"=$SOURCE 30 | LABEL "bitcoin-version"=$VERSION 31 | LABEL "bitcoin-configure-args"="$CONFIGURE_ARGS" 32 | LABEL "git-ref"=${GIT_REF} 33 | LABEL "git-sha"=${GIT_SHA} 34 | LABEL "git-repo-url"=${GIT_REPO_URL} 35 | 36 | COPY ./bin/get-bitcoin /usr/bin/ 37 | RUN chmod +x /usr/bin/get-bitcoin && \ 38 | mkdir /root/bitcoin && \ 39 | get-bitcoin \ 40 | --version "${VERSION}" \ 41 | --git-ref "${GIT_REF}" \ 42 | --git-sha "${GIT_SHA}" \ 43 | --git-repo-url "${GIT_REPO_URL}" \ 44 | --configure-args="${CONFIGURE_ARGS}" \ 45 | --install-prefix /root/bitcoin/ \ 46 | "${SOURCE}" 47 | 48 | 49 | FROM debian:buster-slim 50 | 51 | # Run bitcoin as a non-privileged user to avoid permissions issues with volume mounts, 52 | # amount other things. 53 | # 54 | # These buildargs can be set during container build time with --build-arg UID=[uid] 55 | ARG UID=1000 56 | ARG GID=1000 57 | ARG USERNAME=user 58 | 59 | # Can be "release" or "git" 60 | ARG SOURCE= 61 | # If SOURCE is git, this should be blank. 62 | ARG VERSION= 63 | 64 | ARG GIT_REF= 65 | ARG GIT_SHA= 66 | ARG GIT_REPO_URL= 67 | ARG CONFIGURE_ARGS= 68 | 69 | LABEL "bitcoin-source"=$SOURCE 70 | LABEL "bitcoin-version"=$VERSION 71 | LABEL "bitcoin-configure-args"="$CONFIGURE_ARGS" 72 | LABEL "git-ref"=${GIT_REF} 73 | LABEL "git-sha"=${GIT_SHA} 74 | LABEL "git-repo-url"=${GIT_REPO_URL} 75 | 76 | RUN apt-get update && \ 77 | apt-get install -y iproute2 sudo libevent-pthreads-2.1-6 libzmq5 libsqlite3-0 && \ 78 | rm -rf /var/lib/apt/lists/* 79 | 80 | # Install shared library requirements if we aren't using release binaries 81 | RUN if [ "${SOURCE}" = "git" ]; then \ 82 | apt update && apt install -y \ 83 | libevent-2.1-6 libevent-pthreads-2.1-6 libzmq5 libsqlite3-0 && \ 84 | rm -rf /var/lib/apt/lists/* \ 85 | ; fi 86 | 87 | # Workaround to address https://github.com/jamesob/docker-bitcoind/pull/16 while 88 | # still not running as root user. 89 | COPY ./bin/append-to-hosts.sh /usr/bin/append-to-hosts 90 | RUN chmod +x /usr/bin/append-to-hosts 91 | 92 | # Allow the new user write access to /etc/hosts for the fix in `entrypoint.sh`. 93 | RUN groupadd -g $GID -o $USERNAME && \ 94 | useradd -m -u $UID -g $GID -o -d /home/$USERNAME -s /bin/bash $USERNAME && \ 95 | echo "$USERNAME ALL=(ALL:ALL) NOPASSWD: /usr/bin/append-to-hosts" | tee -a /etc/sudoers 96 | 97 | COPY --from=builder /root/bitcoin/ /usr/local/ 98 | COPY ./entrypoint.sh /usr/local/entrypoint.sh 99 | RUN chmod a+rx /usr/local/entrypoint.sh && \ 100 | mkdir -p /bitcoin/data && \ 101 | chown -R $USERNAME:$GID /bitcoin 102 | 103 | USER $USERNAME 104 | 105 | EXPOSE 8332 8333 18332 18333 28332 28333 106 | 107 | ENTRYPOINT ["/usr/local/entrypoint.sh"] 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # docker-bitcoind 3 | 4 | [![Docker Stars](https://img.shields.io/docker/stars/jamesob/bitcoind.svg)](https://hub.docker.com/r/jamesob/bitcoind/) 5 | [![Docker Pulls](https://img.shields.io/docker/pulls/jamesob/bitcoind.svg)](https://hub.docker.com/r/jamesob/bitcoind/) 6 | 7 | A Docker configuration with sane defaults for running a fully-validating 8 | Bitcoin node. Binaries are retrieved from bitcoincore.org and verified for integrity 9 | based on [the process described here](https://bitcoincore.org/en/download/). 10 | 11 | Optional building from arbitrary git objects is possible (and pretty convenient). 12 | 13 | ## **Warning**: don't trust the Docker registry 14 | 15 | References on the Docker registry (https://hub.docker.com) are mutable. A malicious 16 | actor could change any images hosted there without you realizing it. 17 | 18 | If you use an image served by the Docker registry, ensure that you retrieve 19 | it by its content hash, [as detailed here](https://stackoverflow.com/a/40730725). 20 | Or just build these images yourself. 21 | 22 | With most software this doesn't matter too much, but running an authentic copy of 23 | Bitcoin Core is really important! 24 | 25 | ## **Warning**: don't trust Docker 26 | 27 | Consider whether your use of Bitcoin requires Docker. When you use a container runtime, 28 | you are using a lot of additional code written by other people, e.g. `runc`, `docker`, 29 | potentially `docker-compose`, potentially `podman`. 30 | 31 | Is it necessary to rely on these dependencies? More code running underneath bitcoind 32 | is more chance for someone to meddle with the operation of your node. 33 | 34 | ## **Warning**: don't rely on Dockerfile particulars 35 | 36 | This repo may change Dockerfile implementation. Although the container interface itself 37 | (i.e. volume mounts, operational behavior) will remain stable, the implementation 38 | of how that happens is subject to change. 39 | 40 | If your use relies on the particulars of, for example, the retrieval script 41 | (`get-bitcoin.sh`), please pin your usage of this repo to a particular git hash. 42 | 43 | 44 | ## Tags available 45 | 46 | - 0.13.0 47 | - 0.13.1 48 | - 0.13.2 49 | - 0.14.3 50 | - 0.15.2 51 | - 0.16.3 52 | - 0.17.0 53 | - 0.17.0.1 54 | - 0.17.1 55 | - 0.17.2 56 | - 0.18.0 57 | - 0.18.1 58 | - 0.19.0 59 | - 0.19.1 60 | - 0.20.0 61 | - 0.20.1 62 | - 0.21.0 63 | - 0.21.1 64 | - 0.21.2 65 | - 22.0 66 | - 23.0 67 | 68 | As well as various git refs. 69 | 70 | 71 | ## Labels available 72 | 73 | Each image is built with certain labels: 74 | 75 | - `bitcoin_source`: "release" or "git", depending on how the binaries were built 76 | - `bitcoin_version`: if source=release, the release version (e.g. `23.0`), if 77 | source=git "git:" 78 | - `git_ref`: if source=git, the tag or branch used to build the image 79 | - `git_sha`: if source=git, the specific git commit hash 80 | - `git_repo_url`: if source=git, the repo used to build 81 | 82 | Labels can be shown by running something like 83 | ```sh 84 | % docker image inspect jamesob/bitcoind:master | jq '.[0] .Config .Labels' 85 | ``` 86 | ```json 87 | { 88 | "bitcoin-configure-args": "--enable-reduce-exports --disable-bench --disable-gui-tests --disable-fuzz-binary --disable-ccache --disable-maintainer-mode --disable-dependency-tracking CFLAGS='-O2 -g'", 89 | "bitcoin-source": "git", 90 | "bitcoin-version": "git:master", 91 | "git-ref": "master", 92 | "git-repo-url": "https://github.com/bitcoin/bitcoin", 93 | "git-sha": "551c8e9526d2502f857e1ef6348c7f1380f37443" 94 | } 95 | ``` 96 | 97 | ## Quick start 98 | 99 | Requires that [Docker be installed](https://docs.docker.com/install/) on the host machine. 100 | 101 | ### Autogenerating a config 102 | 103 | ```sh 104 | # Create some directory where your bitcoin data will be stored. 105 | $ mkdir /home/youruser/bitcoin_data 106 | 107 | $ $EDITOR envfile 108 | BTC_RPCPASSWORD=your_password 109 | 110 | $ docker run --name bitcoind -d \ 111 | -e 'BTC_RPCUSER=foo' \ 112 | -e 'BTC_TXINDEX=1' \ 113 | --env-file envfile \ 114 | -v /home/youruser/bitcoin_data:/bitcoin/data \ 115 | -p 127.0.0.1:8332:8332 \ 116 | -p 8333:8333 \ 117 | jamesob/bitcoind:0.20.1 118 | 119 | $ docker logs -f bitcoind 120 | [ ... ] 121 | ``` 122 | 123 | **Warning**: if you specify your RPC password without using an envfile, it may 124 | be captured in your shell history. Use an envfile if you are going to use 125 | `BTC_RPCPASSWORD`. 126 | 127 | If you want the RPC port to be accessible to remote hosts, remove the `127.0.0.1` from 128 | the `-p ...8332` line and set `BTC_RPCBIND=0.0.0.0`. 129 | 130 | ### Using your own config/datadir 131 | 132 | If you want to use a preexisting data directory and your own config file, run 133 | 134 | ```sh 135 | $ docker run --name jamesob/bitcoind:0.20.1 -d \ 136 | -v /home/youruser/bitcoin_data:/bitcoin/data \ 137 | -v /home/youruser/bitcoin.conf:/bitcoin/bitcoin.conf \ 138 | -p 127.0.0.1:8332:8332 \ 139 | -p 8333:8333 \ 140 | jamesob/bitcoind:0.20.1 141 | ``` 142 | 143 | ### Building yourself 144 | 145 | By default, the container runs under UID,GID=1000 to avoid executing as a privileged 146 | user. If you want to rebuild the container with different settings, you can do so: 147 | 148 | ``` 149 | $ git clone https://github.com/jamesob/docker-bitcoind 150 | $ cd docker-bitcoind 151 | $ docker build -t $YOUR_USER/bitcoind:$SOME_VERSION \ 152 | --build-arg UID=$(id -u) \ 153 | --build-arg GID=$(id -g) \ 154 | --build-arg VERSION=$SOME_VERSION \ 155 | --build-arg SOURCE=release \ 156 | . 157 | ``` 158 | 159 | To build an arbitrary git object: 160 | 161 | ``` 162 | $ ./bin/build-docker-bitcoin master 163 | $ ./bin/build-docker-bitcoin v24.0rc2 164 | $ ./bin/build-docker-bitcoin 165 | ``` 166 | 167 | ## Possible volume mounts 168 | 169 | | Path | Description | 170 | | ---- | ------- | 171 | | `/bitcoin/data` | Bitcoin's data directory | 172 | | `/bitcoin/bitcoin.conf` | Bitcoin's configuration file | 173 | 174 | 175 | 176 | ## Configuration 177 | 178 | A custom `bitcoin.conf` file can be placed at `/bitcoin.conf`. 179 | Otherwise, a default will be automatically generated based 180 | on environment variables passed to the container: 181 | 182 | | name | default | 183 | | ---- | ------- | 184 | | BTC_RPCUSER | btc | 185 | | BTC_RPCPASSWORD | | 186 | | BTC_RPCPORT | 8332 | 187 | | BTC_RPCBIND | 127.0.0.1 | 188 | | BTC_RPCALLOWIP | ::/0 | 189 | | BTC_RPCCLIENTTIMEOUT | 30 | 190 | | BTC_DISABLEWALLET | 1 | 191 | | BTC_TXINDEX | 0 | 192 | | BTC_TESTNET | 0 | 193 | | BTC_DBCACHE | 0 | 194 | | BTC_ZMQPUBHASHTX | tcp://0.0.0.0:28333 | 195 | | BTC_ZMQPUBHASHBLOCK | tcp://0.0.0.0:28333 | 196 | | BTC_ZMQPUBRAWTX | tcp://0.0.0.0:28333 | 197 | | BTC_ZMQPUBRAWBLOCK | tcp://0.0.0.0:28333 | 198 | 199 | 200 | ## Daemonizing 201 | 202 | The smart thing to do if you're daemonizing is to use Docker's [builtin restart 203 | policies](https://docs.docker.com/config/containers/start-containers-automatically/#use-a-restart-policy) 204 | (i.e. `docker run --restart unless-stopped ...`), but if you're insistent on using 205 | systemd, you could do something like 206 | 207 | ```bash 208 | $ cat /etc/systemd/system/bitcoind.service 209 | 210 | # bitcoind.service ####################################################################### 211 | [Unit] 212 | Description=Bitcoind 213 | After=docker.service 214 | Requires=docker.service 215 | 216 | [Service] 217 | ExecStartPre=-/usr/bin/docker kill bitcoind 218 | ExecStartPre=-/usr/bin/docker rm bitcoind 219 | ExecStartPre=/usr/bin/docker pull jamesob/bitcoind 220 | ExecStart=/usr/bin/docker run \ 221 | --name bitcoind \ 222 | -p 8333:8333 \ 223 | -p 127.0.0.1:8332:8332 \ 224 | -v /data/bitcoind:/root/.bitcoin \ 225 | jamesob/bitcoind 226 | ExecStop=/usr/bin/docker stop bitcoind 227 | ``` 228 | 229 | to ensure that bitcoind continues to run. 230 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0 2 | -------------------------------------------------------------------------------- /bin/append-to-hosts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Included as a workaround to address https://github.com/jamesob/docker-bitcoind/pull/16 4 | # 5 | echo "$@" >> /etc/hosts 6 | -------------------------------------------------------------------------------- /bin/build-docker-bitcoin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import os 5 | import sys 6 | import importlib.util 7 | from functools import lru_cache 8 | from pathlib import Path 9 | 10 | import fscm 11 | from fscm import run 12 | from clii import App 13 | 14 | cli = App() 15 | 16 | fscm.settings.run_safe = True 17 | 18 | DEFAULT_REPO_PATH = Path.home() / "src" / "bitcoin-reference" 19 | REPO_URL = "https://github.com/bitcoin/bitcoin" 20 | DOCKER_USERNAME = "jamesob" 21 | 22 | # From https://github.com/bitcoin/bitcoin/blob/master/contrib/guix/libexec/build.sh 23 | DEFAULT_CONFIG_ARGS = ( 24 | "--enable-reduce-exports --disable-bench --disable-gui-tests --disable-fuzz-binary " 25 | "--disable-ccache --disable-maintainer-mode --disable-dependency-tracking " 26 | # Guix usually includes `-g` here and manually strips debug symbols. 27 | "CFLAGS='-O2'" 28 | ) 29 | 30 | 31 | @lru_cache 32 | def docker_login(): 33 | run("docker login docker.io") 34 | 35 | 36 | @cli.cmd 37 | def git( 38 | git_ref: str, 39 | configure_args: str = DEFAULT_CONFIG_ARGS, 40 | docker_username: str = DOCKER_USERNAME, 41 | repo_path: str = str(DEFAULT_REPO_PATH), # type: ignore 42 | push: bool = False, 43 | tag: str = "", 44 | ): 45 | """ 46 | Build a container image from the git repository. 47 | Args: 48 | git_ref: the git ref to build, e.g. "master" or "jamesob/utxo-dumpload-compressed" 49 | 50 | Kwargs: 51 | docker_repo_name: the docker username to push the container into 52 | repo_path: where the cached bitcoin repo will live 53 | """ 54 | repo_path: Path = Path(repo_path) 55 | if not repo_path.exists(): 56 | run(f"git clone {REPO_URL} {repo_path}") 57 | 58 | remote = "origin" 59 | if "/" in git_ref: 60 | remote, git_ref = git_ref.split("/", 1) 61 | elif ':' in git_ref: 62 | remote, git_ref = git_ref.split(":", 1) 63 | 64 | remote_url = "https://github.com/bitcoin/bitcoin" 65 | if remote != "origin": 66 | remote_url = f"https://github.com/{remote}/bitcoin" 67 | 68 | os.chdir(repo_path) 69 | 70 | remotes = {l.split()[0] for l in run("git remote -v", q=True).stdout.splitlines()} 71 | 72 | if remote not in remotes: 73 | run(f"git remote add {remote} https://github.com/{remote}/bitcoin") 74 | print(f"Added remote {remote}") 75 | 76 | run("git fetch --all", q=True) 77 | 78 | try: 79 | sha = run(f"git rev-list -n 1 {remote}/{git_ref}", q=True).stdout.strip() 80 | except fscm.CommandFailure: 81 | # Try a tag 82 | sha = run(f"git rev-list -n 1 {git_ref}", q=True).stdout.strip() 83 | 84 | print(f"Resolved git ref {remote}/{git_ref} to {sha}") 85 | 86 | os.chdir(str(fscm.this_dir_path().parent)) 87 | 88 | if tag: 89 | tags = [tag] 90 | else: 91 | tags = [ 92 | f"{docker_username}/bitcoind:{git_ref}", 93 | f"{docker_username}/bitcoind:{sha}", 94 | ] 95 | if git_ref == "master": 96 | tags.append(f"{docker_username}/bitcoind:master-latest") 97 | 98 | tag_str = " ".join(f"-t {t}" for t in tags) 99 | 100 | run( 101 | f"docker build {tag_str} " 102 | '--build-arg "SOURCE=git" ' 103 | f'--build-arg "VERSION=git:{git_ref}" ' 104 | f'--build-arg "GIT_REF={git_ref}" ' 105 | f'--build-arg "GIT_SHA={sha}" ' 106 | f'--build-arg "CONFIGURE_ARGS={configure_args}" ' 107 | f'--build-arg "GIT_REPO_URL={remote_url}" ' 108 | "." 109 | ) 110 | 111 | print(f"Built container for {remote}/{git_ref} ({sha}) as {tags[0]}") 112 | 113 | labels = json.loads(run(f"docker image inspect {tags[0]}", q=True).stdout)[0][ 114 | "Config" 115 | ]["Labels"] 116 | 117 | try: 118 | # Sanity check the labels 119 | assert labels["bitcoin-source"] == "git" 120 | assert labels["bitcoin-version"] == f"git:{git_ref}" 121 | assert labels["bitcoin-configure-args"] == configure_args 122 | assert labels["git-sha"] == sha 123 | assert labels["git-ref"] == git_ref 124 | assert labels["git-repo-url"] == remote_url 125 | except AssertionError: 126 | print(f"Labels mismatch; expected {labels}") 127 | sys.exit(1) 128 | 129 | if push: 130 | docker_login() 131 | for tag in tags: 132 | run(f"docker push {tag}") 133 | 134 | return tags[0] 135 | 136 | 137 | @cli.cmd 138 | def release(version: str, push: bool = False, tag: str = ""): 139 | """ 140 | Build a container image from a bitcoincore.org release. 141 | """ 142 | tag = tag or f"jamesob/bitcoind:{version}" 143 | run( 144 | f'docker build -t "{tag}" ' 145 | '--build-arg "SOURCE=release" ' 146 | f'--build-arg "VERSION={version}" ' 147 | "." 148 | ) 149 | 150 | if push: 151 | docker_login() 152 | run(f"docker push {tag}") 153 | 154 | return tag 155 | 156 | 157 | @cli.cmd 158 | def all_releases(after_version: str = ""): 159 | """ 160 | Build a container for all releases 161 | """ 162 | gb = fscm.this_dir_path() / "get-bitcoin" 163 | spec = importlib.util.spec_from_loader("getbitcoin", loader=None) 164 | assert spec 165 | getbitcoin = importlib.util.module_from_spec(spec) 166 | exec(gb.read_text(), getbitcoin.__dict__) 167 | 168 | for ver in getbitcoin.RELEASE_VERSIONS: 169 | if after_version and ver < after_version: 170 | continue 171 | 172 | release(ver, push=True) 173 | 174 | 175 | if __name__ == "__main__": 176 | cli.run() 177 | -------------------------------------------------------------------------------- /bin/get-bitcoin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # vim: ft=python 3 | """ 4 | A script for obtaining Bitcoin Core binaries, either from a release or building 5 | from source. 6 | """ 7 | 8 | import argparse 9 | import sys 10 | import os 11 | import subprocess 12 | from pathlib import Path 13 | 14 | 15 | def sh(cmd, *args, **kwargs): 16 | kwargs.setdefault("shell", True) 17 | kwargs.setdefault("check", True) 18 | kwargs.setdefault("text", True) 19 | return subprocess.run(cmd, *args, **kwargs) 20 | 21 | 22 | def err(*args, **kwargs): 23 | kwargs["file"] = sys.stderr 24 | print(*args, **kwargs) 25 | 26 | 27 | RELEASE_VERSIONS = ( 28 | "0.13.0", 29 | "0.13.1", 30 | "0.13.2", 31 | "0.14.3", 32 | "0.15.2", 33 | "0.16.3", 34 | "0.17.0", 35 | "0.17.0.1", 36 | "0.17.1", 37 | "0.17.2", 38 | "0.18.0", 39 | "0.18.1", 40 | "0.19.0.1", 41 | "0.19.1", 42 | "0.20.0", 43 | "0.20.1", 44 | "0.20.2", 45 | "0.21.0", 46 | "0.21.1", 47 | "0.21.2", 48 | "22.0", 49 | "23.0", 50 | ) 51 | 52 | 53 | def prechecks(): 54 | if not (Path("/etc/debian_version").exists() or Path("/etc/lsb_release").exists()): 55 | err("This script is intended for use on Debian-based systems.") 56 | sys.exit(3) 57 | 58 | 59 | def from_release(version: str, install_prefix: str): 60 | if version not in RELEASE_VERSIONS: 61 | versions = "\n".join(f" {v}" for v in RELEASE_VERSIONS) 62 | err(f"Version {version} not available. Choose from:\n\n{versions}") 63 | sys.exit(2) 64 | 65 | prechecks() 66 | URL_BASE = f"https://bitcoincore.org/bin/bitcoin-core-{version}" 67 | FILENAME = f"bitcoin-{version}-x86_64-linux-gnu.tar.gz" 68 | 69 | # Verify signing key fingerprints here: 70 | # 71 | # https://github.com/bitcoin/bitcoin/tree/master/contrib/builder-keys 72 | 73 | sh(f'curl -O "{URL_BASE}/SHA256SUMS.asc"') 74 | sh(f'curl -O "{URL_BASE}/{FILENAME}"') 75 | 76 | vertuple = tuple(int(i) for i in version.split(".")) 77 | 78 | if vertuple < (22, 0): 79 | # In version 22.0, release signing changed from a single key signing in 80 | # SHA256SUMS.asc to multiple keys signing SHA256SUMS. 81 | # 82 | # See here for more information: https://github.com/bitcoin/bitcoin/pull/23020 83 | 84 | sh( 85 | "gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys " 86 | "01EA5486DE18A882D4C2684590C8019E36C2E964" 87 | ) 88 | sh( 89 | "sha256sum --ignore-missing --check SHA256SUMS.asc " 90 | f' | tee - | grep -o "{FILENAME}: OK"' 91 | ) 92 | sh("gpg --verify SHA256SUMS.asc >gpg_verify_out 2>&1") 93 | sh("grep '^gpg: Good signature from \"Wladimir J. van der Laan' gpg_verify_out") 94 | sh( 95 | "grep '^Primary key fingerprint: 01EA 5486 DE18 A882 D4C2 6845 90C8 019E 36C2 E964' " 96 | "gpg_verify_out" 97 | ) 98 | else: 99 | # See bitcoin/contrib/builder-keys/keys.txt for current values. 100 | # 101 | # I've chosen a subset of builder keys here who are well-known and reliably 102 | # sign for releases. 103 | 104 | KEYS = ( 105 | # Wladimir 106 | "71A3B16735405025D447E8F274810B012346C9A6", 107 | # Hebasto 108 | "D1DBF2C4B96F2DEBF4C16654410108112E7EA81F", 109 | # Fanquake 110 | "E777299FC265DD04793070EB944D35F9AC3DB76A", 111 | ) 112 | 113 | for key in KEYS: 114 | sh(f"gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys {key}") 115 | 116 | VERIFY = { 117 | "Wladimir J. van der Laan": "71A3 B167 3540 5025 D447 E8F2 7481 0B01 2346 C9A6", 118 | "Hennadii Stepanov": "D1DB F2C4 B96F 2DEB F4C1 6654 4101 0811 2E7E A81F", 119 | "Michael Ford": "E777 299F C265 DD04 7930 70EB 944D 35F9 AC3D B76A", 120 | } 121 | 122 | sh(f'curl -O "{URL_BASE}/SHA256SUMS"') 123 | sh("gpg --verify SHA256SUMS.asc SHA256SUMS >gpg_verify_out 2>&1 || true") 124 | sh("cat gpg_verify_out") 125 | 126 | for name, fp in VERIFY.items(): 127 | sh(f"grep '^gpg: Good signature from \"{name}' gpg_verify_out") 128 | sh(f"grep '^Primary key fingerprint: {fp}' gpg_verify_out") 129 | 130 | sh( 131 | f'sha256sum --ignore-missing --check SHA256SUMS | tee - | grep -o "{FILENAME}: OK"' 132 | ) 133 | 134 | sh(f"tar -xzvf {FILENAME}") 135 | dir = sh( 136 | "find . -name 'bitcoin-*' -type d | head -n 1", capture_output=True 137 | ).stdout.strip() 138 | sh(f"ls -lah {dir}") 139 | sh(f"rm {dir}/bin/bitcoin-qt") 140 | sh(f"cp -r {dir}/* {install_prefix}") 141 | 142 | print("\nBitcoin installed:\n") 143 | 144 | check = True 145 | if vertuple < (0, 14): 146 | check = False 147 | 148 | verout = sh( 149 | f"{install_prefix}/bin/bitcoind -version", 150 | check=check, 151 | capture_output=True, 152 | ).stdout 153 | assert " version " in verout 154 | assert version in verout 155 | 156 | 157 | def from_git( 158 | gitref: str, 159 | gitsha: str, 160 | repo_url: str, 161 | configure_args: str, 162 | install_prefix: str, 163 | no_strip_binaries: bool = False, 164 | ): 165 | sh(f"git clone {repo_url} /bitcoin") 166 | os.chdir("/bitcoin") 167 | sh(f"git checkout {gitref}") 168 | 169 | gotsha = sh("git rev-list -n 1 HEAD", capture_output=True).stdout.strip() 170 | if gotsha != gitsha: 171 | err(f"Git SHA doesn't match: got '{gotsha}', expected '{gitsha}'") 172 | sys.exit(2) 173 | 174 | sh("./autogen.sh") 175 | sh(f'./configure {configure_args}') 176 | sh("make -j $(nproc --ignore 1)") 177 | sh(f"mkdir -p {install_prefix}/bin") 178 | sh( 179 | "cp src/bitcoind src/bitcoin-cli src/bitcoin-tx src/bitcoin-util " 180 | f"src/bitcoin-wallet src/test/test_bitcoin {install_prefix}/bin" 181 | ) 182 | 183 | if not no_strip_binaries: 184 | binpath = Path(install_prefix) / "bin" 185 | os.chdir(binpath) 186 | for p in binpath.glob("*"): 187 | sh(f"strip --enable-deterministic-archives -p -s {p.name}") 188 | 189 | 190 | def getparser(): 191 | ap = argparse.ArgumentParser() 192 | ao = ap.add_argument 193 | 194 | ao("source", help='either "release" or "git"') 195 | ao("--install-prefix", help="where to move the binaries", default=".") 196 | 197 | ao( 198 | "--version", 199 | help="if obtaining from release, which version to obtain", 200 | default="", 201 | ) 202 | 203 | ao("--git-ref", help="if obtaining from git, which tag/branch to build", default="") 204 | ao("--git-sha", help="if obtaining from git, which commit to build", default="") 205 | ao("--git-repo-url", default="https://github.com/bitcoin/bitcoin") 206 | ao("--configure-args", help=f"pass replacement ./configure args") 207 | 208 | return ap 209 | 210 | 211 | if __name__ == "__main__": 212 | parser = getparser() 213 | args = parser.parse_args() 214 | 215 | if args.source == "release": 216 | from_release(args.version, args.install_prefix) 217 | elif args.source == "git": 218 | from_git( 219 | args.git_ref, 220 | args.git_sha, 221 | args.git_repo_url, 222 | args.configure_args, 223 | args.install_prefix, 224 | ) 225 | else: 226 | err(f"source '{args.source}' unrecognized. choices: release, git") 227 | sys.exit(1) 228 | -------------------------------------------------------------------------------- /bin/get-bitcoin.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | # For debugging: 5 | # set -x 6 | 7 | # Versions available (per https://bitcoincore.org/bin) are: 8 | # 9 | # (there are earlier versions available, but the binary URLs don't conform to the 10 | # same pattern.) 11 | # 12 | 13 | VERSIONS=( 14 | 0.13.0 15 | 0.13.1 16 | 0.13.2 17 | 0.14.3 18 | 0.15.2 19 | 0.16.3 20 | 0.17.0 21 | 0.17.0.1 22 | 0.17.1 23 | 0.17.2 24 | 0.18.0 25 | 0.18.1 26 | 0.19.0.1 27 | 0.19.1 28 | 0.20.0 29 | 0.20.1 30 | 0.20.2 31 | 0.21.0 32 | 0.21.1 33 | 0.21.2 34 | 22.0 35 | 23.0 36 | ) 37 | 38 | err() { 39 | >&2 echo "$@" 40 | } 41 | 42 | err "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" 43 | err "" 44 | err "THIS SCRIPT IS DEPRECATED AND UNMAINTAINED. See ./bin/get-bitcoin." 45 | err "" 46 | err "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" 47 | 48 | 49 | if [ ! -f /etc/debian_version ] && [ ! -f /etc/lsb_release ]; then 50 | err "This script is intended for use on Debian-based systems." 51 | exit 1 52 | fi 53 | 54 | VERSION="$1" 55 | INSTALL_PREFIX="${2:-/}" 56 | 57 | URL_BASE="https://bitcoincore.org/bin/bitcoin-core-${VERSION}" 58 | FILENAME="bitcoin-${VERSION}-x86_64-linux-gnu.tar.gz" 59 | 60 | if [ -z "${VERSION}" ]; then 61 | err "Usage: get-bitcoin.sh []" 62 | err 63 | err "Available versions are:" 64 | 65 | for v in "${VERSIONS[@]}"; do 66 | err " ${v}" 67 | done 68 | 69 | err 70 | exit 1 71 | fi 72 | 73 | set -x 74 | 75 | TMPDIR=$(mktemp -d) 76 | cd "$TMPDIR" 77 | 78 | # Verify signing key fingerprints here: 79 | # 80 | # https://github.com/bitcoin/bitcoin/tree/master/contrib/builder-keys 81 | 82 | curl -O "${URL_BASE}/SHA256SUMS.asc" 83 | curl -O "${URL_BASE}/${FILENAME}" 84 | 85 | # In version 22.0, release signing changed from a single key signing in 86 | # SHA256SUMS.asc to multiple keys signing SHA256SUMS. 87 | # 88 | # See here for more information: https://github.com/bitcoin/bitcoin/pull/23020 89 | 90 | if [[ "$VERSION" < "22.0" ]]; then 91 | gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 01EA5486DE18A882D4C2684590C8019E36C2E964 92 | sha256sum --ignore-missing --check SHA256SUMS.asc \ 93 | | tee - | grep -o "${FILENAME}: OK" 94 | gpg --verify SHA256SUMS.asc >gpg_verify_out 2>&1 95 | grep '^gpg: Good signature from "Wladimir J. van der Laan' gpg_verify_out 96 | grep '^Primary key fingerprint: 01EA 5486 DE18 A882 D4C2 6845 90C8 019E 36C2 E964' gpg_verify_out 97 | 98 | else 99 | # See bitcoin/contrib/builder-keys/keys.txt for current values. 100 | # 101 | # I've chosen a subset of builder keys here who are well-known and reliably 102 | # sign for releases. 103 | 104 | # Wladimir 105 | gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 71A3B16735405025D447E8F274810B012346C9A6 106 | # Hebasto 107 | gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys D1DBF2C4B96F2DEBF4C16654410108112E7EA81F 108 | # Fanquake 109 | gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys E777299FC265DD04793070EB944D35F9AC3DB76A 110 | 111 | curl -O "${URL_BASE}/SHA256SUMS" 112 | gpg --verify SHA256SUMS.asc SHA256SUMS >gpg_verify_out 2>&1 || true 113 | cat gpg_verify_out 114 | 115 | grep '^gpg: Good signature from "Wladimir J. van der Laan' gpg_verify_out 116 | grep '^Primary key fingerprint: 71A3 B167 3540 5025 D447 E8F2 7481 0B01 2346 C9A6' gpg_verify_out 117 | 118 | grep '^gpg: Good signature from "Hennadii Stepanov' gpg_verify_out 119 | grep '^Primary key fingerprint: D1DB F2C4 B96F 2DEB F4C1 6654 4101 0811 2E7E A81F' gpg_verify_out 120 | 121 | grep '^gpg: Good signature from "Michael Ford' gpg_verify_out 122 | grep '^Primary key fingerprint: E777 299F C265 DD04 7930 70EB 944D 35F9 AC3D B76A' gpg_verify_out 123 | 124 | sha256sum --ignore-missing --check SHA256SUMS | tee - | grep -o "${FILENAME}: OK" 125 | fi 126 | 127 | tar -xzvf "${FILENAME}" 128 | DIR=$(find . -name 'bitcoin-*' -type d | head -n 1) 129 | ls -lah ${DIR} 130 | rm "${DIR}"/bin/bitcoin-qt 131 | cp -r "${DIR}"/* "${INSTALL_PREFIX}" 132 | 133 | echo 134 | echo "Bitcoin installed:" 135 | echo 136 | "${INSTALL_PREFIX}/bin/bitcoind" --version || true 137 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | export PATH=/usr/local/bin:$PATH 6 | 7 | # See https://github.com/jamesob/docker-bitcoind/pull/16 8 | sudo /usr/bin/append-to-hosts "$(ip -4 route list match 0/0 | awk '{print $3 "\thost.docker.internal"}')" 9 | 10 | BITCOIN_DIR=/bitcoin/data 11 | BITCOIN_CONF=/bitcoin/bitcoin.conf 12 | 13 | if [ -z "${BTC_RPCPASSWORD:-}" ]; then 14 | # Provide a random password. 15 | BTC_RPCPASSWORD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 24 ; echo '') 16 | fi 17 | 18 | umask 077 19 | 20 | # If config doesn't exist, initialize with sane defaults for running a 21 | # non-mining node. 22 | 23 | if [ ! -e "${BITCOIN_CONF}" ]; then 24 | tee -a >${BITCOIN_CONF} <