├── .gitignore ├── test ├── tests │ ├── rpcpassword │ │ ├── run.sh │ │ └── container.sh │ ├── run-bash-in-container.sh │ ├── image-name.sh │ ├── docker-build.sh │ └── run-in-container.sh ├── config.sh ├── README.md └── run.sh ├── bin ├── btc_oneshot └── btc_init ├── .travis.yml ├── docker-entrypoint.sh ├── init ├── upstart.init └── docker-bitcoind.service ├── docs ├── build.md ├── debug.md └── config.md ├── LICENSE ├── bootstrap-host.sh ├── CONTRIBUTING.md ├── README.md └── Dockerfile /.gitignore: -------------------------------------------------------------------------------- 1 | /official-images 2 | -------------------------------------------------------------------------------- /test/tests/rpcpassword/run.sh: -------------------------------------------------------------------------------- 1 | ../run-bash-in-container.sh -------------------------------------------------------------------------------- /test/config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | testAlias+=( 5 | [bitcoind:trusty]='bitcoind' 6 | ) 7 | 8 | imageTests+=( 9 | [bitcoind]=' 10 | rpcpassword 11 | ' 12 | ) 13 | -------------------------------------------------------------------------------- /test/tests/run-bash-in-container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | testDir="$(readlink -f "$(dirname "$BASH_SOURCE")")" 5 | runDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" 6 | 7 | source "$runDir/run-in-container.sh" "$testDir" "$1" bash ./container.sh 8 | -------------------------------------------------------------------------------- /test/tests/image-name.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # usage: ./image-name.sh librarytest/something some/image:some-tag 5 | # output: librarytest/something:some-image-some-tag 6 | 7 | base="$1"; shift 8 | tag="$1"; shift 9 | 10 | echo "$base:$(echo "$tag" | sed 's![:/]!-!g')" 11 | -------------------------------------------------------------------------------- /bin/btc_oneshot: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | # Generate bitcoin.conf 6 | btc_init 7 | 8 | # Default / no argument invocation listens for RPC commands and has to accept non-localhost because of 9 | # Docker port proxying or Docker private networking. 10 | if [ $# -eq 0 ]; then 11 | # If IPv6 is in the container do both: 12 | #set -- '-rpcbind=[::]:8332' '-rpcallowip=::/0' '-rpcallowip=0.0.0.0/0' 13 | set -- '-rpcbind=:8332' '-rpcallowip=0.0.0.0/0' 14 | fi 15 | 16 | exec bitcoind "$@" 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | services: docker 3 | 4 | env: 5 | - VARIANT=xenial 6 | 7 | install: 8 | - git clone https://github.com/docker-library/official-images.git official-images 9 | 10 | before_script: 11 | - env | sort 12 | - image="bitcoind:$VARIANT" 13 | 14 | script: 15 | - docker buildx build --platform linux/arm64,linux/amd64,linux/riscv64 -t "$image" . 16 | - official-images/test/run.sh "$image" 17 | - test/run.sh "$image" 18 | 19 | after_script: 20 | - docker images 21 | 22 | # vim:set et ts=2 sw=2: 23 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # first arg is `-f` or `--some-option` 5 | # or first arg is `something.conf` 6 | if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then 7 | set -- btc_oneshot "$@" 8 | fi 9 | 10 | # Allow the container to be started with `--user`, if running as root drop privileges 11 | if [ "$1" = 'btc_oneshot' -a "$(id -u)" = '0' ]; then 12 | chown -R bitcoin . 13 | exec gosu bitcoin "$0" "$@" 14 | fi 15 | 16 | # If not root (i.e. docker run --user $USER ...), then run as invoked 17 | exec "$@" 18 | 19 | -------------------------------------------------------------------------------- /init/upstart.init: -------------------------------------------------------------------------------- 1 | # Copy to /etc/init/docker-bitcoind.conf 2 | description "Docker container for bitcoind p2p node" 3 | start on filesystem and started docker 4 | stop on runlevel [!2345] 5 | respawn 6 | pre-start script 7 | # Clean-up if left behind 8 | docker rm -f bitcoind-node || true 9 | 10 | # Attempt to always update and fail silently if the network isn't ready 11 | docker pull kylemanna/bitcoind || true 12 | end script 13 | script 14 | exec docker run --rm -v bitcoind-data:/bitcoin --name=bitcoind-node --rm -p 8333:8333 -p 127.0.0.1:8332:8332 kylemanna/bitcoind 15 | end script 16 | -------------------------------------------------------------------------------- /bin/btc_init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | # This shouldn't be in the Dockerfile or containers built from the same image 6 | # will have the same credentials. 7 | if [ ! -e "$HOME/.bitcoin/bitcoin.conf" ]; then 8 | mkdir -p $HOME/.bitcoin 9 | 10 | echo "Creating bitcoin.conf" 11 | 12 | # Seed a random password for JSON RPC server 13 | cat < $HOME/.bitcoin/bitcoin.conf 14 | regtest=${REGTEST:-0} 15 | disablewallet=${DISABLEWALLET:-1} 16 | printtoconsole=${PRINTTOCONSOLE:-1} 17 | rpcuser=${RPCUSER:-bitcoinrpc} 18 | rpcpassword=${RPCPASSWORD:-`dd if=/dev/urandom bs=33 count=1 2>/dev/null | base64`} 19 | EOF 20 | 21 | fi 22 | 23 | cat $HOME/.bitcoin/bitcoin.conf 24 | 25 | echo "Initialization completed successfully" 26 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | Philosophy is to not re-invent the wheel while allowing users to quickly test repository specific tests. 4 | 5 | Example invocation from top-level of repository: 6 | 7 | docker build -t bitcoind . 8 | tests/run.sh bitcoind 9 | 10 | More details: https://github.com/docker-library/official-images/tree/master/test 11 | 12 | ## Continuous Integration 13 | 14 | The set of scripts defined by `config.sh` are run every time a pull request or push to the repository is made. 15 | 16 | ## Maintenance 17 | 18 | Periodically these scripts may need to be synchronized with their upsteam source. Would be nice to be able to just use them from upstream if it such a feature is added later to avoid having to copy them in place. 19 | -------------------------------------------------------------------------------- /docs/build.md: -------------------------------------------------------------------------------- 1 | Building 2 | ======== 3 | 4 | The image can be built from source by running: 5 | 6 | docker build . 7 | 8 | A recommended security practice is to add an additional unprivileged user to run the daemon as on the host. For example, as a privileged user, run this on the host: 9 | 10 | useradd bitcoind 11 | 12 | To build an image which uses this unprivileged user's id and group id, run: 13 | 14 | docker build --build-arg USER_ID=$( id -u bitcoind ) --build-arg GROUP_ID=$( id -g bitcoind ) . 15 | 16 | Now, when the container is run with the default options, the bitcoind process will only have the privileges of the bitcoind user on the host machine. This is especially important for a process such as bitcoind which runs as a network service exposed to the internet. 17 | -------------------------------------------------------------------------------- /test/tests/rpcpassword/container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Generate the password the first time 5 | btc_init 2>/dev/null 6 | eval `grep rpcpassword $HOME/.bitcoin/bitcoin.conf` 7 | rpcpassword1=$rpcpassword 8 | 9 | # Generate the password again 10 | rm ~/.bitcoin/bitcoin.conf 11 | btc_init 2>/dev/null 12 | eval `grep rpcpassword $HOME/.bitcoin/bitcoin.conf` 13 | rpcpassword2=$rpcpassword 14 | 15 | 16 | # Check that password looks like a auto-generated base64 random value or better 17 | if [ ${#rpcpassword} -lt 16 ]; then 18 | echo "FAIL: RPC Password does not appear long enough" >&2 19 | exit 1 20 | fi 21 | 22 | # Check that each password was at least different 23 | if [ "$rpcpassword1" = "$rpcpassword2" ]; then 24 | echo "FAIL: RPC Password does not appear to be random" >&2 25 | exit 2 26 | fi 27 | -------------------------------------------------------------------------------- /docs/debug.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | ## Things to Check 4 | 5 | * RAM utilization -- bitcoind is very hungry and typically needs in excess of 1GB. A swap file might be necessary. 6 | * Disk utilization -- The bitcoin blockchain will continue growing and growing and growing. Then it will grow some more. At the time of writing, 40GB+ is necessary. 7 | 8 | ## Viewing bitcoind Logs 9 | 10 | docker logs bitcoind-node 11 | 12 | 13 | ## Running Bash in Docker Container 14 | 15 | *Note:* This container will be run in the same way as the bitcoind node, but will not connect to already running containers or processes. 16 | 17 | docker run -v bitcoind-data:/bitcoin --rm -it kylemanna/bitcoind bash -l 18 | 19 | You can also attach bash into running container to debug running bitcoind 20 | 21 | docker exec -it bitcoind-node bash -l 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/config.md: -------------------------------------------------------------------------------- 1 | bitcoind config tuning 2 | ====================== 3 | 4 | You can use environment variables to customize config ([see docker run environment options](https://docs.docker.com/engine/reference/run/#/env-environment-variables)): 5 | 6 | docker run -v bitcoind-data:/bitcoin/.bitcoin --name=bitcoind-node -d \ 7 | -p 8333:8333 \ 8 | -p 127.0.0.1:8332:8332 \ 9 | -e REGTEST=0 \ 10 | -e DISABLEWALLET=1 \ 11 | -e PRINTTOCONSOLE=1 \ 12 | -e RPCUSER=mysecretrpcuser \ 13 | -e RPCPASSWORD=mysecretrpcpassword \ 14 | kylemanna/bitcoind 15 | 16 | Or you can use your very own config file like that: 17 | 18 | docker run -v bitcoind-data:/bitcoin/.bitcoin --name=bitcoind-node -d \ 19 | -p 8333:8333 \ 20 | -p 127.0.0.1:8332:8332 \ 21 | -v /etc/mybitcoin.conf:/bitcoin/.bitcoin/bitcoin.conf \ 22 | kylemanna/bitcoind 23 | -------------------------------------------------------------------------------- /init/docker-bitcoind.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Bitcoind Docker Container 3 | Documentation=https://github.com/kylemanna/docker-bitcoind 4 | Requires=docker.service 5 | After=docker.service 6 | 7 | [Service] 8 | RestartSec=10 9 | Restart=always 10 | 11 | # Modify IP6_PREFIX to match network config 12 | Environment="NAME=bitcoind" 13 | Environment="DATA_VOL=bitcoind-data" 14 | Environment="IMG=kylemanna/bitcoind:dev" 15 | Environment="PORT=8333:8333/tcp" 16 | 17 | # To override environment variables, use local configuration directory: 18 | # /etc/systemd/system/docker-bitcoind.service/local.conf 19 | # http://www.freedesktop.org/software/systemd/man/systemd.unit.html 20 | 21 | # Clean-up bad state if still hanging around 22 | ExecStartPre=-/usr/bin/docker rm -f $NAME 23 | 24 | # Attempt to pull new image for security updates 25 | ExecStartPre=-/usr/bin/docker pull $IMG 26 | 27 | # Main process 28 | ExecStart=/usr/bin/docker run --rm -v ${DATA_VOL}:/bitcoin --name ${NAME} -p ${PORT} ${IMG} btc_oneshot $ARGS 29 | 30 | [Install] 31 | WantedBy=multi-user.target 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kyle Manna 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /test/tests/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # wrapper around "docker build" that creates a temporary directory and copies files into it first so that arbitrary host directories can be copied into containers without bind mounts, but accepts a Dockerfile on stdin 5 | 6 | # usage: ./docker-build.sh some-host-directory some-new-image:some-tag < "$tmp/Dockerfile" 25 | 26 | from="$(awk -F '[ \t]+' 'toupper($1) == "FROM" { print $2; exit }' "$tmp/Dockerfile")" 27 | onbuilds="$(docker inspect -f '{{len .Config.OnBuild}}' "$from")" 28 | if [ "$onbuilds" -gt 0 ]; then 29 | # crap, the image we want to build has some ONBUILD instructions 30 | # those are kind of going to ruin our day 31 | # let's do some hacks to strip those bad boys out in a new fake layer 32 | "$(dirname "$(readlink -f "$BASH_SOURCE")")/remove-onbuild.sh" "$from" "$imageTag" 33 | awk -F '[ \t]+' 'toupper($1) == "FROM" { $2 = "'"$imageTag"'" } { print }' "$tmp/Dockerfile" > "$tmp/Dockerfile.new" 34 | mv "$tmp/Dockerfile.new" "$tmp/Dockerfile" 35 | fi 36 | 37 | cp -RL "$dir" "$tmp/dir" 38 | 39 | docker build -t "$imageTag" "$tmp" > /dev/null 40 | -------------------------------------------------------------------------------- /test/tests/run-in-container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # NOT INTENDED TO BE USED AS A TEST "run.sh" DIRECTLY 5 | # SEE OTHER "run-*-in-container.sh" SCRIPTS FOR USAGE 6 | 7 | testDir="$1" 8 | shift 9 | 10 | image="$1" 11 | shift 12 | entrypoint="$1" 13 | shift 14 | 15 | # do some fancy footwork so that if testDir is /a/b/c, we mount /a/b and use c as the working directory (so relative symlinks work one level up) 16 | thisDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" 17 | testDir="$(readlink -f "$testDir")" 18 | testBase="$(basename "$testDir")" 19 | hostMount="$(dirname "$testDir")" 20 | containerMount="/tmp/test-dir" 21 | workdir="$containerMount/$testBase" 22 | # TODO should we be doing something fancy with $BASH_SOURCE instead so we can be arbitrarily deep and mount the top level always? 23 | 24 | newImage="$("$thisDir/image-name.sh" librarytest/run-in-container "$image--$testBase")" 25 | "$thisDir/docker-build.sh" "$hostMount" "$newImage" <> /etc/fstab 21 | swapon -a 22 | fi 23 | 24 | free -m 25 | 26 | if [ "$distro" = "trusty" -o "$distro" = "ubuntu:14.04" ]; then 27 | curl https://get.docker.io/gpg | apt-key add - 28 | echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list 29 | 30 | # Handle other parallel cloud init scripts that may lock the package database 31 | # TODO: Add timeout 32 | while ! apt-get update; do sleep 10; done 33 | 34 | while ! apt-get install -y lxc-docker; do sleep 10; done 35 | fi 36 | 37 | # Always clean-up, but fail successfully 38 | docker kill bitcoind-node 2>/dev/null || true 39 | docker rm bitcoind-node 2>/dev/null || true 40 | stop docker-bitcoind 2>/dev/null || true 41 | 42 | # Always pull remote images to avoid caching issues 43 | if [ -z "${BTC_IMAGE##*/*}" ]; then 44 | docker pull $BTC_IMAGE 45 | fi 46 | 47 | # Initialize the data container 48 | docker volume create --name=bitcoind-data 49 | docker run -v bitcoind-data:/bitcoin --rm $BTC_IMAGE btc_init 50 | 51 | # Start bitcoind via upstart and docker 52 | curl https://raw.githubusercontent.com/kylemanna/docker-bitcoind/master/upstart.init > /etc/init/docker-bitcoind.conf 53 | start docker-bitcoind 54 | 55 | set +ex 56 | echo "Resulting bitcoin.conf:" 57 | docker run -v bitcoind-data:/bitcoin --rm $BTC_IMAGE cat /bitcoin/.bitcoin/bitcoin.conf 58 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to docker-bitcoind 2 | 3 | Community contributions are welcome and help move the project along. Please review this document before sending any pull requests. 4 | 5 | Thanks! 6 | 7 | ## Bug Fixes 8 | 9 | All bug fixes are welcome. Please try to add a test if the bug is something that should have been fixed already. Oops. 10 | 11 | ## Feature Additions 12 | 13 | New features are welcome provided that the feature has a general audience and is reasonably simple. The goal of the repository is to support a wide audience and be simple enough. 14 | 15 | Please add new documentation in the `docs` folder for any new features. Pull requests for missing documentation is welcome as well. Keep the `README.md` focused on the most popular use case, details belong in the docs directory. 16 | 17 | If you have a special feature, you're likely to try but it will likely be rejected if not too many people seem interested. 18 | 19 | ## Tests 20 | 21 | In an effort to not repeat bugs (and break less popular features), unit tests are run on [Travis CI](https://travis-ci.org/kylemanna/docker-bitcoind). The goal of the tests are to be simple and to be placed in the `test` directory where it will be automatically run. 22 | 23 | See [test directory](https://github.com/kylemanna/docker-bitcoind/tree/master/test) for details. 24 | 25 | ## Style 26 | 27 | The style of the repo follows that of the Linux kernel, in particular: 28 | 29 | * Pull requests should be rebased to small atomic commits so that the merged history is more coherent 30 | * The subject of the commit should be in the form ": " 31 | * More details in the body 32 | * Match surrounding coding style (line wrapping, spaces, etc) 33 | 34 | More details in the [SubmittingPatches](https://www.kernel.org/doc/html/latest/process/submitting-patches.html) document included with the Linux kernel. In particular the following sections: 35 | 36 | * `2) Describe your changes` 37 | * `3) Separate your changes` 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Bitcoind for Docker 2 | =================== 3 | 4 | [![Docker Stars](https://img.shields.io/docker/stars/kylemanna/bitcoind.svg)](https://hub.docker.com/r/kylemanna/bitcoind/) 5 | [![Docker Pulls](https://img.shields.io/docker/pulls/kylemanna/bitcoind.svg)](https://hub.docker.com/r/kylemanna/bitcoind/) 6 | [![Build Status](https://travis-ci.org/kylemanna/docker-bitcoind.svg?branch=master)](https://travis-ci.org/kylemanna/docker-bitcoind/) 7 | 8 | Docker image that runs the Bitcoin bitcoind node in a container for easy deployment. 9 | 10 | 11 | Requirements 12 | ------------ 13 | 14 | * Physical machine, cloud instance, or VPS that supports Docker (i.e. [Vultr](http://bit.ly/1HngXg0), [Digital Ocean](http://bit.ly/18AykdD), KVM or XEN based VMs) running Ubuntu 14.04 or later (*not OpenVZ containers!*) 15 | * At least 700 GB to store the block chain files (and always growing!) 16 | * At least 1 GB RAM + 2 GB swap file 17 | 18 | Recommended and tested on unadvertised (only shown within control panel) [Vultr SATA Storage 1024 MB RAM/250 GB disk instance @ $10/mo](http://bit.ly/vultrbitcoind). Vultr also *accepts Bitcoin payments*! 19 | 20 | 21 | Really Fast Quick Start 22 | ----------------------- 23 | 24 | One liner for Ubuntu 14.04 LTS machines with JSON-RPC enabled on localhost and adds upstart init script: 25 | 26 | curl https://raw.githubusercontent.com/kylemanna/docker-bitcoind/master/bootstrap-host.sh | sh -s trusty 27 | 28 | 29 | Quick Start 30 | ----------- 31 | 32 | 1. Create a `bitcoind-data` volume to persist the bitcoind blockchain data, should exit immediately. The `bitcoind-data` container will store the blockchain when the node container is recreated (software upgrade, reboot, etc): 33 | 34 | docker volume create --name=bitcoind-data 35 | docker run -v bitcoind-data:/bitcoin/.bitcoin --name=bitcoind-node -d \ 36 | -p 8333:8333 \ 37 | -p 127.0.0.1:8332:8332 \ 38 | kylemanna/bitcoind 39 | 40 | 2. Verify that the container is running and bitcoind node is downloading the blockchain 41 | 42 | $ docker ps 43 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 44 | d0e1076b2dca kylemanna/bitcoind:latest "btc_oneshot" 2 seconds ago Up 1 seconds 127.0.0.1:8332->8332/tcp, 0.0.0.0:8333->8333/tcp bitcoind-node 45 | 46 | 3. You can then access the daemon's output thanks to the [docker logs command](https://docs.docker.com/reference/cli/docker/service/logs/) 47 | 48 | docker logs -f bitcoind-node 49 | 50 | 4. Install optional init scripts for upstart and systemd are in the `init` directory. 51 | 52 | 53 | Documentation 54 | ------------- 55 | 56 | * Additional documentation in the [docs folder](docs). 57 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Smallest base image, latests stable image 2 | # Alpine would be nice, but it's linked again musl and breaks the bitcoin core download binary 3 | #FROM alpine:latest 4 | 5 | FROM ubuntu:latest AS builder 6 | ARG TARGETARCH 7 | 8 | FROM builder AS builder_amd64 9 | ENV ARCH=x86_64 10 | FROM builder AS builder_arm64 11 | ENV ARCH=aarch64 12 | FROM builder AS builder_riscv64 13 | ENV ARCH=riscv64 14 | 15 | FROM builder_${TARGETARCH} AS build 16 | 17 | # Testing: gosu 18 | #RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing/" >> /etc/apk/repositories \ 19 | # && apk add --update --no-cache gnupg gosu gcompat libgcc 20 | RUN apt update \ 21 | && apt install -y --no-install-recommends \ 22 | ca-certificates \ 23 | gnupg \ 24 | libatomic1 \ 25 | wget \ 26 | && apt clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 27 | 28 | ARG VERSION=29.1 29 | ARG BITCOIN_CORE_SIGNATURE=71A3B16735405025D447E8F274810B012346C9A6 30 | 31 | # Don't use base image's bitcoin package for a few reasons: 32 | # 1. Would need to use ppa/latest repo for the latest release. 33 | # 2. Some package generates /etc/bitcoin.conf on install and that's dangerous to bake in with Docker Hub. 34 | # 3. Verifying pkg signature from main website should inspire confidence and reduce chance of surprises. 35 | # Instead fetch, verify, and extract to Docker image 36 | RUN cd /tmp \ 37 | && gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys ${BITCOIN_CORE_SIGNATURE} \ 38 | && wget https://bitcoincore.org/bin/bitcoin-core-${VERSION}/SHA256SUMS.asc \ 39 | https://bitcoincore.org/bin/bitcoin-core-${VERSION}/SHA256SUMS \ 40 | https://bitcoincore.org/bin/bitcoin-core-${VERSION}/bitcoin-${VERSION}-${ARCH}-linux-gnu.tar.gz \ 41 | && gpg --verify --status-fd 1 --verify SHA256SUMS.asc SHA256SUMS 2>/dev/null | grep "^\[GNUPG:\] VALIDSIG.*${BITCOIN_CORE_SIGNATURE}\$" \ 42 | && sha256sum --ignore-missing --check SHA256SUMS \ 43 | && tar -xzvf bitcoin-${VERSION}-${ARCH}-linux-gnu.tar.gz -C /opt \ 44 | && ln -sv bitcoin-${VERSION} /opt/bitcoin \ 45 | && /opt/bitcoin/bin/test_bitcoin --show_progress \ 46 | && rm -v /opt/bitcoin/bin/test_bitcoin /opt/bitcoin/bin/bitcoin-qt 47 | 48 | FROM ubuntu:latest 49 | LABEL maintainer="Kyle Manna " 50 | 51 | ENTRYPOINT ["docker-entrypoint.sh"] 52 | ENV HOME=/bitcoin 53 | EXPOSE 8332 8333 54 | VOLUME ["/bitcoin/.bitcoin"] 55 | WORKDIR /bitcoin 56 | 57 | ARG GROUP_ID=1000 58 | ARG USER_ID=1000 59 | RUN userdel ubuntu \ 60 | && groupadd -g ${GROUP_ID} bitcoin \ 61 | && useradd -u ${USER_ID} -g bitcoin -d /bitcoin bitcoin 62 | 63 | COPY --from=build /opt/ /opt/ 64 | 65 | RUN apt update \ 66 | && apt install -y --no-install-recommends gosu libatomic1 \ 67 | && apt clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ 68 | && ln -sv /opt/bitcoin/bin/* /usr/local/bin 69 | 70 | COPY ./bin ./docker-entrypoint.sh /usr/local/bin/ 71 | 72 | CMD ["btc_oneshot"] 73 | -------------------------------------------------------------------------------- /test/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | dir="$(dirname "$(readlink -f "$BASH_SOURCE")")" 5 | 6 | self="$(basename "$0")" 7 | 8 | usage() { 9 | cat <&2 && false; })" 23 | eval set -- "$opts" 24 | 25 | declare -A argTests=() 26 | declare -a configs=() 27 | dryRun= 28 | while true; do 29 | flag=$1 30 | shift 31 | case "$flag" in 32 | --dry-run) dryRun=1 ;; 33 | --help|-h|'-?') usage && exit 0 ;; 34 | --test|-t) argTests["$1"]=1 && shift ;; 35 | --config|-c) configs+=("$(readlink -f "$1")") && shift ;; 36 | --) break ;; 37 | *) 38 | { 39 | echo "error: unknown flag: $flag" 40 | usage 41 | } >&2 42 | exit 1 43 | ;; 44 | esac 45 | done 46 | 47 | if [ $# -eq 0 ]; then 48 | usage >&2 49 | exit 1 50 | fi 51 | 52 | # declare configuration variables 53 | declare -a globalTests=() 54 | declare -A testAlias=() 55 | declare -A imageTests=() 56 | declare -A globalExcludeTests=() 57 | declare -A explicitTests=() 58 | 59 | # if there are no user-specified configs, use the default config 60 | if [ ${#configs} -eq 0 ]; then 61 | configs+=("$dir/config.sh") 62 | fi 63 | 64 | # load the configs 65 | declare -A testPaths=() 66 | for conf in "${configs[@]}"; do 67 | . "$conf" 68 | 69 | # Determine the full path to any newly-declared tests 70 | confDir="$(dirname "$conf")" 71 | 72 | for testName in ${globalTests[@]} ${imageTests[@]}; do 73 | [ "${testPaths[$testName]}" ] && continue 74 | 75 | if [ -d "$confDir/tests/$testName" ]; then 76 | # Test directory found relative to the conf file 77 | testPaths[$testName]="$confDir/tests/$testName" 78 | elif [ -d "$dir/tests/$testName" ]; then 79 | # Test directory found in the main tests/ directory 80 | testPaths[$testName]="$dir/tests/$testName" 81 | fi 82 | done 83 | done 84 | 85 | didFail= 86 | for dockerImage in "$@"; do 87 | echo "testing $dockerImage" 88 | 89 | if ! docker inspect "$dockerImage" &> /dev/null; then 90 | echo $'\timage does not exist!' 91 | didFail=1 92 | continue 93 | fi 94 | 95 | repo="${dockerImage%:*}" 96 | tagVar="${dockerImage#*:}" 97 | #version="${tagVar%-*}" 98 | variant="${tagVar##*-}" 99 | 100 | testRepo=$repo 101 | [ -z "${testAlias[$repo]}" ] || testRepo="${testAlias[$repo]}" 102 | 103 | explicitVariant= 104 | if [ \ 105 | "${explicitTests[:$variant]}" \ 106 | -o "${explicitTests[$repo:$variant]}" \ 107 | -o "${explicitTests[$testRepo:$variant]}" \ 108 | ]; then 109 | explicitVariant=1 110 | fi 111 | 112 | testCandidates=() 113 | if [ -z "$explicitVariant" ]; then 114 | testCandidates+=( "${globalTests[@]}" ) 115 | fi 116 | testCandidates+=( 117 | ${imageTests[:$variant]} 118 | ) 119 | if [ -z "$explicitVariant" ]; then 120 | testCandidates+=( 121 | ${imageTests[$testRepo]} 122 | ) 123 | fi 124 | testCandidates+=( 125 | ${imageTests[$testRepo:$variant]} 126 | ) 127 | if [ "$testRepo" != "$repo" ]; then 128 | if [ -z "$explicitVariant" ]; then 129 | testCandidates+=( 130 | ${imageTests[$repo]} 131 | ) 132 | fi 133 | testCandidates+=( 134 | ${imageTests[$repo:$variant]} 135 | ) 136 | fi 137 | 138 | tests=() 139 | for t in "${testCandidates[@]}"; do 140 | if [ ${#argTests[@]} -gt 0 -a -z "${argTests[$t]}" ]; then 141 | # skipping due to -t 142 | continue 143 | fi 144 | 145 | if [ \ 146 | ! -z "${globalExcludeTests[${testRepo}_$t]}" \ 147 | -o ! -z "${globalExcludeTests[${testRepo}:${variant}_$t]}" \ 148 | -o ! -z "${globalExcludeTests[:${variant}_$t]}" \ 149 | -o ! -z "${globalExcludeTests[${repo}_$t]}" \ 150 | -o ! -z "${globalExcludeTests[${repo}:${variant}_$t]}" \ 151 | -o ! -z "${globalExcludeTests[:${variant}_$t]}" \ 152 | ]; then 153 | # skipping due to exclude 154 | continue 155 | fi 156 | 157 | tests+=( "$t" ) 158 | done 159 | 160 | currentTest=0 161 | totalTest="${#tests[@]}" 162 | for t in "${tests[@]}"; do 163 | (( currentTest+=1 )) 164 | echo -ne "\t'$t' [$currentTest/$totalTest]..." 165 | 166 | # run test against dockerImage here 167 | # find the script for the test 168 | scriptDir="${testPaths[$t]}" 169 | if [ -d "$scriptDir" ]; then 170 | script="$scriptDir/run.sh" 171 | if [ -x "$script" -a ! -d "$script" ]; then 172 | # TODO dryRun logic 173 | if output="$("$script" $dockerImage)"; then 174 | if [ -f "$scriptDir/expected-std-out.txt" ] && ! d="$(echo "$output" | diff -u "$scriptDir/expected-std-out.txt" - 2>/dev/null)"; then 175 | echo 'failed; unexpected output:' 176 | echo "$d" 177 | didFail=1 178 | else 179 | echo 'passed' 180 | fi 181 | else 182 | echo 'failed' 183 | didFail=1 184 | fi 185 | else 186 | echo "skipping" 187 | echo >&2 "error: $script missing, not executable or is a directory" 188 | didFail=1 189 | continue 190 | fi 191 | else 192 | echo "skipping" 193 | echo >&2 "error: unable to locate test '$t'" 194 | didFail=1 195 | continue 196 | fi 197 | done 198 | done 199 | 200 | if [ "$didFail" ]; then 201 | exit 1 202 | fi 203 | --------------------------------------------------------------------------------