|
| 1 | +# Running with `SEED_CHAINSTATE=true` will require the container to be launched with `--shm-size=xxxxMB` where at least 256MB is recommended |
| 2 | +ARG STACKS_API_VERSION=v7.1.10 |
| 3 | +ARG STACKS_BLOCKCHAIN_VERSION=2.3.0.0.2 |
| 4 | +ARG PG_VERSION=15 |
| 5 | +ARG STACKS_NETWORK=mainnet |
| 6 | +ARG PG_HOST=127.0.0.1 |
| 7 | +ARG PG_PORT=5432 |
| 8 | +ARG PG_USER=postgres |
| 9 | +ARG PG_PASSWORD=postgres |
| 10 | +ARG SEED_CHAINSTATE=false |
| 11 | +ARG ARCHIVE_VERSION=latest |
| 12 | + |
| 13 | +####################################################################### |
| 14 | +## Build the stacks-blockchain-api |
| 15 | +FROM node:16-buster as stacks-blockchain-api-build |
| 16 | +ARG STACKS_API_VERSION |
| 17 | +ENV STACKS_API_REPO=hirosystems/stacks-blockchain-api |
| 18 | +ENV STACKS_API_VERSION=${STACKS_API_VERSION} |
| 19 | +ENV DEBIAN_FRONTEND noninteractive |
| 20 | +WORKDIR /app |
| 21 | +RUN apt-get update -y \ |
| 22 | + && apt-get install -y \ |
| 23 | + curl \ |
| 24 | + jq \ |
| 25 | + openjdk-11-jre-headless \ |
| 26 | + cmake \ |
| 27 | + && git clone -b ${STACKS_API_VERSION} https://github.com/${STACKS_API_REPO} . \ |
| 28 | + && echo "GIT_TAG=$(git tag --points-at HEAD)" >> .env \ |
| 29 | + && npm config set unsafe-perm true \ |
| 30 | + && npm ci \ |
| 31 | + && npm run build \ |
| 32 | + && npm prune --production |
| 33 | + |
| 34 | +####################################################################### |
| 35 | +## Build the stacks-blockchain |
| 36 | +FROM rust:buster as stacks-blockchain-build |
| 37 | +ARG STACKS_BLOCKCHAIN_VERSION |
| 38 | +ENV STACKS_NODE_REPO=stacks-network/stacks-blockchain |
| 39 | +ENV STACKS_BLOCKCHAIN_VERSION=${STACKS_BLOCKCHAIN_VERSION} |
| 40 | +ENV DEBIAN_FRONTEND noninteractive |
| 41 | +WORKDIR /src |
| 42 | +RUN apt-get update -y \ |
| 43 | + && apt-get install -y \ |
| 44 | + curl \ |
| 45 | + && mkdir -p /out \ |
| 46 | + && git clone -b ${STACKS_BLOCKCHAIN_VERSION} --depth 1 https://github.com/${STACKS_NODE_REPO} . \ |
| 47 | + && cd testnet/stacks-node \ |
| 48 | + && cargo build --features monitoring_prom,slog_json --release \ |
| 49 | + && cp /src/target/release/stacks-node /out |
| 50 | + |
| 51 | +####################################################################### |
| 52 | +## Build the final image with all components from build stages |
| 53 | +FROM debian:buster |
| 54 | +ARG STACKS_NETWORK |
| 55 | +ARG PG_HOST |
| 56 | +ARG PG_PORT |
| 57 | +ARG PG_USER |
| 58 | +ARG PG_PASSWORD |
| 59 | +ARG PG_VERSION |
| 60 | +ARG SEED_CHAINSTATE |
| 61 | +ARG STACKS_API_VERSION |
| 62 | +ARG STACKS_BLOCKCHAIN_VERSION |
| 63 | +ARG ARCHIVE_VERSION |
| 64 | +ENV SEED_CHAINSTATE=${SEED_CHAINSTATE} |
| 65 | +ENV STACKS_API_VERSION=${STACKS_API_VERSION} |
| 66 | +ENV STACKS_BLOCKCHAIN_VERSION=${STACKS_BLOCKCHAIN_VERSION} |
| 67 | +ENV PG_VERSION=${PG_VERSION} |
| 68 | +ENV PG_HOST=${PG_HOST} |
| 69 | +ENV PG_PORT=${PG_PORT} |
| 70 | +ENV PG_USER=${PG_USER} |
| 71 | +ENV PG_PASSWORD=${PG_PASSWORD} |
| 72 | +ENV PG_DATABASE=stacks_blockchain_api |
| 73 | +ENV PGDATA=/postgres |
| 74 | +ENV PG_SCHEMA=stacks_blockchain_api |
| 75 | +ENV STACKS_BLOCKCHAIN_DIR=/stacks-blockchain |
| 76 | +ENV STACKS_BLOCKCHAIN_API_DIR=/stacks-blockchain-api |
| 77 | +ENV STACKS_NETWORK=${STACKS_NETWORK} |
| 78 | +ENV STACKS_CORE_EVENT_PORT=3700 |
| 79 | +ENV STACKS_CORE_EVENT_HOST=127.0.0.1 |
| 80 | +ENV STACKS_EVENT_OBSERVER=127.0.0.1:3700 |
| 81 | +ENV STACKS_BLOCKCHAIN_API_PORT=3999 |
| 82 | +ENV STACKS_BLOCKCHAIN_API_HOST=0.0.0.0 |
| 83 | +ENV STACKS_CORE_RPC_HOST=127.0.0.1 |
| 84 | +ENV STACKS_CORE_RPC_PORT=20443 |
| 85 | +ENV STACKS_CORE_P2P_PORT=20444 |
| 86 | +ENV ARCHIVE_VERSION=${ARCHIVE_VERSION} |
| 87 | +ENV LANG en_US.UTF-8 |
| 88 | +ENV LANGUAGE en_US:en |
| 89 | +ENV LC_ALL en_US.UTF-8 |
| 90 | +ENV DEBIAN_FRONTEND noninteractive |
| 91 | + |
| 92 | +RUN apt-get update \ |
| 93 | + && apt-get install -y \ |
| 94 | + gnupg2 \ |
| 95 | + lsb-release \ |
| 96 | + curl \ |
| 97 | + jq \ |
| 98 | + procps \ |
| 99 | + netcat \ |
| 100 | + gosu \ |
| 101 | + locales |
| 102 | +RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen |
| 103 | + |
| 104 | +RUN curl -sL https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ |
| 105 | + && echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" > /etc/apt/sources.list.d/pgsql.list \ |
| 106 | + && curl -sL https://deb.nodesource.com/setup_16.x | bash - |
| 107 | +RUN apt-get update \ |
| 108 | + && apt-get install -y \ |
| 109 | + postgresql-${PG_VERSION} \ |
| 110 | + postgresql-client-${PG_VERSION} \ |
| 111 | + nodejs \ |
| 112 | + && echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections |
| 113 | +RUN mkdir -p \ |
| 114 | + ${STACKS_BLOCKCHAIN_DIR}/data \ |
| 115 | + /scripts \ |
| 116 | + && apt-get clean \ |
| 117 | + && rm -rf /var/cache/apt/* /var/lib/apt/lists/* /tmp/* |
| 118 | +COPY --from=stacks-blockchain-build /out ${STACKS_BLOCKCHAIN_DIR} |
| 119 | +COPY --from=stacks-blockchain-api-build /app ${STACKS_BLOCKCHAIN_API_DIR} |
| 120 | +COPY --from=stacks-blockchain-build /src/testnet/stacks-node/conf/*follower-conf.toml ${STACKS_BLOCKCHAIN_DIR}/ |
| 121 | + |
| 122 | +################################### |
| 123 | +## entrypoint.sh |
| 124 | +RUN <<EOF |
| 125 | +cat > /entrypoint.sh <<'EOM' |
| 126 | +#!/bin/bash -e |
| 127 | +exec 2>&1 |
| 128 | +# enable json logging for stacks-blockchain |
| 129 | +export STACKS_LOG_JSON=1 |
| 130 | +# configure postgres and start it |
| 131 | +mkdir -p "${PGDATA}" || exit 1 |
| 132 | +chown -R postgres:postgres "${PGDATA}" || exit 1 |
| 133 | +gosu postgres /usr/lib/postgresql/${PG_VERSION}/bin/initdb -D "${PGDATA}" --wal-segsize=512 || exit 1 |
| 134 | +echo "host all all all trust" >> "$PGDATA/pg_hba.conf" || exit 1 |
| 135 | +gosu postgres /usr/lib/postgresql/${PG_VERSION}/bin/pg_ctl start -w -D ${PGDATA} -o "-c listen_addresses='*'" || exit 1 |
| 136 | + |
| 137 | +# download archive files if flag is true |
| 138 | +if [ "${SEED_CHAINSTATE}" = "true" ]; then |
| 139 | + /scripts/seed-chainstate.sh || exit 1 |
| 140 | +fi |
| 141 | +# create DB/schema if using other than default 'postgres' |
| 142 | +if [[ "${SEED_CHAINSTATE}" = "false" && "${PG_DATABASE}" != "postgres" ]]; then |
| 143 | + /scripts/postgres-initdb.sh || exit 1 |
| 144 | +fi |
| 145 | +# set chain_id based on network |
| 146 | +case "${STACKS_NETWORK}" in |
| 147 | + testnet) |
| 148 | + export STACKS_CHAIN_ID=0x80000000 |
| 149 | + ;; |
| 150 | + *) |
| 151 | + export STACKS_CHAIN_ID=0x00000001 |
| 152 | + ;; |
| 153 | +esac |
| 154 | +# start stacks-blockchain and store pid |
| 155 | +${STACKS_BLOCKCHAIN_DIR}/stacks-node start --config=${STACKS_BLOCKCHAIN_DIR}/${STACKS_NETWORK}-follower-conf.toml 2>&1 & |
| 156 | +STACKS_BLOCKCHAIN_PID=$! |
| 157 | + |
| 158 | +# start stacks-blockchain-api and store pid |
| 159 | +pushd /stacks-blockchain-api |
| 160 | +node ./lib/index.js 2>&1 & |
| 161 | +STACKS_API_PID=$! |
| 162 | + |
| 163 | +# try to stop processes gracefully |
| 164 | +function cleanup() { |
| 165 | + echo "Exiting, signal: $1" |
| 166 | + kill $STACKS_PID 2>/dev/null && echo "stacks-blockchain exiting.." |
| 167 | + wait $STACKS_PID 2>/dev/null && echo "stacks-blockchain exited" |
| 168 | + kill $API_PID 2>/dev/null && echo "stacks-blockchain-api exiting.." |
| 169 | + wait $API_PID 2>/dev/null && echo "stacks-blockchain-api exited" |
| 170 | + echo "Postgres exiting.." |
| 171 | + gosu postgres /usr/lib/postgresql/${PG_VERSION}/bin/pg_ctl stop -W -D "$PGDATA" 2>/dev/null && echo "Postgres exited" |
| 172 | +} |
| 173 | +trap "cleanup SIGTERM" SIGTERM |
| 174 | +trap "cleanup SIGINT" SIGINT |
| 175 | +trap "cleanup SIGHUP" SIGHUP |
| 176 | +trap "cleanup EXIT" EXIT |
| 177 | +wait |
| 178 | +EOM |
| 179 | +chmod +x /entrypoint.sh |
| 180 | +EOF |
| 181 | + |
| 182 | +################################### |
| 183 | +## /scripts/postgres-initdb.sh |
| 184 | +RUN <<EOF |
| 185 | +cat > /scripts/postgres-initdb.sh <<'EOM' |
| 186 | +#!/bin/bash -e |
| 187 | +# connect to postgres and create DB/schema as defined in env vars |
| 188 | +psql -v ON_ERROR_STOP=1 --username "${PG_USER}" --dbname "template1" <<-EOSQL |
| 189 | + SELECT 'CREATE DATABASE ${PG_DATABASE}' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${PG_DATABASE}')\gexec |
| 190 | + \c ${PG_DATABASE}; |
| 191 | + CREATE SCHEMA IF NOT EXISTS ${PG_SCHEMA}; |
| 192 | + GRANT ALL PRIVILEGES ON DATABASE ${PG_DATABASE} TO ${PG_USER}; |
| 193 | +EOSQL |
| 194 | +exit 0 |
| 195 | +EOM |
| 196 | +chmod +x /scripts/postgres-initdb.sh |
| 197 | +EOF |
| 198 | + |
| 199 | +################################### |
| 200 | +## /scripts/seed-chainstate.sh |
| 201 | +RUN <<EOF |
| 202 | +cat > /scripts/seed-chainstate.sh <<'EOM' |
| 203 | +#!/bin/bash -e |
| 204 | +exec 2>&1 |
| 205 | +echo "Seeding chainstate from https://archive.hiro.so" |
| 206 | +# remove the "v" in front of the API version since the archive files do not use this naming structure |
| 207 | +LOCAL_STACKS_API_VERSION=$(echo "${STACKS_API_VERSION:1}") |
| 208 | + |
| 209 | +# define URL's to download |
| 210 | +PGDUMP_URL="https://archive.hiro.so/${STACKS_NETWORK}/stacks-blockchain-api-pg/stacks-blockchain-api-pg-${PG_VERSION}-${LOCAL_STACKS_API_VERSION}-${ARCHIVE_VERSION}.dump" |
| 211 | +PGDUMP_URL_SHA256="https://archive.hiro.so/${STACKS_NETWORK}/stacks-blockchain-api-pg/stacks-blockchain-api-pg-${PG_VERSION}-${LOCAL_STACKS_API_VERSION}-${ARCHIVE_VERSION}.sha256" |
| 212 | +CHAINDATA_URL="https://archive.hiro.so/${STACKS_NETWORK}/stacks-blockchain/${STACKS_NETWORK}-stacks-blockchain-${STACKS_BLOCKCHAIN_VERSION}-${ARCHIVE_VERSION}.tar.gz" |
| 213 | +CHAINDATA_URL_SHA256="https://archive.hiro.so/${STACKS_NETWORK}/stacks-blockchain/${STACKS_NETWORK}-stacks-blockchain-${STACKS_BLOCKCHAIN_VERSION}-${ARCHIVE_VERSION}.sha256" |
| 214 | + |
| 215 | +# define local storage locations |
| 216 | +PGDUMP_DEST="/tmp/stacks-blockchain-api-pg-${PG_VERSION}-${LOCAL_STACKS_API_VERSION}-${ARCHIVE_VERSION}.dump" |
| 217 | +PGDUMP_DEST_SHA256="/tmp/stacks-blockchain-api-pg-${PG_VERSION}-${LOCAL_STACKS_API_VERSION}-${ARCHIVE_VERSION}.sha256" |
| 218 | +CHAINDATA_DEST="/tmp/${STACKS_NETWORK}-stacks-blockchain-${STACKS_BLOCKCHAIN_VERSION}-${ARCHIVE_VERSION}.tar.gz" |
| 219 | +CHAINDATA_DEST_SHA256="/tmp/${STACKS_NETWORK}-stacks-blockchain-${STACKS_BLOCKCHAIN_VERSION}-${ARCHIVE_VERSION}.sha256" |
| 220 | + |
| 221 | +exit_error() { |
| 222 | + echo "${1}" |
| 223 | + exit 1 |
| 224 | +} |
| 225 | + |
| 226 | +download_file(){ |
| 227 | + # download the archive file if it exists. if not, exit with error |
| 228 | + local url=${1} |
| 229 | + local dest=${2} |
| 230 | + # retrieve http code of archive file |
| 231 | + local http_code=$(curl --output /dev/null --silent --head -w "%{http_code}" ${url}) |
| 232 | + # if file does noe exist, exit |
| 233 | + if [[ "${http_code}" && "${http_code}" != "200" ]];then |
| 234 | + exit_error "Error ${url} not found" |
| 235 | + fi |
| 236 | + echo "Downloading ${url} data to: ${dest}" |
| 237 | + curl -L -# ${url} -o "${dest}" || exit_error "Error downloading ${url} to ${dest}" |
| 238 | + return 0 |
| 239 | +} |
| 240 | + |
| 241 | +verify_checksum(){ |
| 242 | + # compares local sha256sum with downloaded sha256sum file |
| 243 | + local local_file=${1} |
| 244 | + local local_sha256=${2} |
| 245 | + local sha256=$(cat ${local_sha256} | awk {'print $1'} ) |
| 246 | + local basename=$(basename ${local_file}) |
| 247 | + local sha256sum=$(sha256sum ${local_file} | awk {'print $1'}) |
| 248 | + # if sha256sum does not match file, exit |
| 249 | + if [ "${sha256}" != "${sha256sum}" ]; then |
| 250 | + exit_error "Error sha256 mismatch for ${basename}" |
| 251 | + fi |
| 252 | + return 0 |
| 253 | +} |
| 254 | + |
| 255 | +# download the pg_dump archive and verify the sha256sum matches |
| 256 | +download_file ${PGDUMP_URL} ${PGDUMP_DEST} |
| 257 | +download_file ${PGDUMP_URL_SHA256} ${PGDUMP_DEST_SHA256} |
| 258 | +verify_checksum ${PGDUMP_DEST} ${PGDUMP_DEST_SHA256} |
| 259 | + |
| 260 | +# download the chainstate archive and verify the sha256sum matches |
| 261 | +download_file ${CHAINDATA_URL} ${CHAINDATA_DEST} |
| 262 | +download_file ${CHAINDATA_URL_SHA256} ${CHAINDATA_DEST_SHA256} |
| 263 | +verify_checksum ${CHAINDATA_DEST} ${CHAINDATA_DEST_SHA256} |
| 264 | + |
| 265 | +# restore the pg_dump |
| 266 | +psql -U ${PG_USER} -c "alter user ${PG_USER} set max_parallel_workers_per_gather=0;" || exit_error "error altering user" |
| 267 | +pg_restore --username ${PG_USER} --verbose --create --dbname postgres ${PGDUMP_DEST} || exit_error "Error restoring API pg_dump data" |
| 268 | +psql -U ${PG_USER} -c "ALTER USER ${PG_USER} PASSWORD '${PG_PASSWORD}';" || exit_error "Error setting PG_USER password" |
| 269 | +psql -U ${PG_USER} -c "GRANT ALL PRIVILEGES ON DATABASE $PG_DATABASE TO $PG_USER;" || exit_error "error granting PG_USER privileges" |
| 270 | + |
| 271 | +# update stacks-blockchain config to use extracted data, then extract it |
| 272 | +sed -i -e "s|^# working_dir.*|working_dir = \"${STACKS_BLOCKCHAIN_DIR}/data\"|;" ${STACKS_BLOCKCHAIN_DIR}/${STACKS_NETWORK}-follower-conf.toml || exit_error "error updating stacks-blockchain config" |
| 273 | +echo "Extracting stacks-blockchain chainstate data to: ${STACKS_BLOCKCHAIN_DIR}/data" |
| 274 | +tar -xvf "${CHAINDATA_DEST}" -C "${STACKS_BLOCKCHAIN_DIR}/data" || exit_error "Error extracting stacks-blockchain chainstate data" |
| 275 | + |
| 276 | +# remove downloaded files to reduce disk usage |
| 277 | +if [ -f ${PGDUMP_DEST} ]; then |
| 278 | + rm -f ${PGDUMP_DEST} || exit_error "Error removing ${PGDUMP_DEST}" |
| 279 | +fi |
| 280 | +if [ -f ${PGDUMP_DEST_SHA256} ]; then |
| 281 | + rm -f ${PGDUMP_DEST_SHA256} || exit_error "Error removing ${PGDUMP_DEST_SHA256}" |
| 282 | +fi |
| 283 | +if [ -f ${CHAINDATA_DEST} ]; then |
| 284 | + rm -f ${CHAINDATA_DEST} || exit_error "Error removing ${CHAINDATA_DEST}" |
| 285 | +fi |
| 286 | +if [ -f ${CHAINDATA_DEST_SHA256} ]; then |
| 287 | + rm -f ${CHAINDATA_DEST_SHA256} || exit_error "Error removing ${CHAINDATA_DEST_SHA256}" |
| 288 | +fi |
| 289 | +exit 0 |
| 290 | +EOM |
| 291 | +chmod +x /scripts/seed-chainstate.sh |
| 292 | +EOF |
| 293 | + |
| 294 | + |
| 295 | + |
| 296 | +EXPOSE ${STACKS_BLOCKCHAIN_API_PORT} ${STACKS_CORE_RPC_PORT} ${STACKS_CORE_P2P_PORT} |
| 297 | +VOLUME /data |
| 298 | +CMD ["/entrypoint.sh"] |
| 299 | + |
0 commit comments