diff --git a/bitcoin-knots/data/app/.gitkeep b/bitcoin-knots/data/app/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/bitcoin-knots/data/bitcoin/.gitkeep b/bitcoin-knots/data/bitcoin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/bitcoin-knots/data/i2pd/.gitkeep b/bitcoin-knots/data/i2pd/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/bitcoin-knots/docker-compose.yml b/bitcoin-knots/docker-compose.yml new file mode 100644 index 0000000..8e0dc04 --- /dev/null +++ b/bitcoin-knots/docker-compose.yml @@ -0,0 +1,79 @@ +version: "3.7" + +services: + app_proxy: + environment: + APP_HOST: $APP_BITCOIN_KNOTS_IP + APP_PORT: 3005 + + server: + image: retropexx/umbrel-bitcoin-knots:v0.5.0@sha256:b19da8308ec9b6c66511d327f5fd2b64aa430a3da8ed3854e8e170274b11b624 + depends_on: [bitcoind] + restart: on-failure + volumes: + - ${APP_DATA_DIR}/data/app:/data # volume to persist advanced settings json + - ${APP_BITCOIN_KNOTS_DATA_DIR}:/bitcoin/.bitcoin # volume to persist umbrel-bitcoin.conf and bitcoin.conf + environment: + PORT: "3005" + BITCOIN_HOST: "${APP_BITCOIN_KNOTS_NODE_IP}" + RPC_PORT: "${APP_BITCOIN_KNOTS_INTERNAL_RPC_PORT}" + BITCOIN_RPC_PORT: "${APP_BITCOIN_KNOTS_INTERNAL_RPC_PORT}" + RPC_USER: "${APP_BITCOIN_KNOTS_RPC_USER}" + BITCOIN_RPC_USER: "${APP_BITCOIN_KNOTS_RPC_USER}" + RPC_PASSWORD: "${APP_BITCOIN_KNOTS_RPC_PASS}" + BITCOIN_RPC_PASSWORD: "${APP_BITCOIN_KNOTS_RPC_PASS}" + BITCOIN_RPC_HIDDEN_SERVICE: "${APP_BITCOIN_KNOTS_RPC_HIDDEN_SERVICE}" + BITCOIN_P2P_HIDDEN_SERVICE: "${APP_BITCOIN_KNOTS_P2P_HIDDEN_SERVICE}" + BITCOIN_P2P_PORT: "${APP_BITCOIN_KNOTS_P2P_PORT}" + DEVICE_DOMAIN_NAME: "${DEVICE_DOMAIN_NAME}" + BITCOIN_DEFAULT_NETWORK: "${BITCOIN_KNOTS_DEFAULT_NETWORK:-mainnet}" + # not needed + BITCOIN_INITIALIZE_WITH_CLEARNET_OVER_TOR: "${BITCOIN_INITIALIZE_WITH_CLEARNET_OVER_TOR:-unset}" + BITCOIND_IP: "${APP_BITCOIN_KNOTS_NODE_IP}" + TOR_PROXY_IP: "${APP_BITCOIN_KNOTS_TOR_PROXY_IP}" + TOR_PROXY_PORT: "9050" + TOR_PROXY_CONTROL_PORT: "9051" + TOR_PROXY_CONTROL_PASSWORD: "moneyprintergobrrr" + I2P_DAEMON_IP: "${APP_BITCOIN_KNOTS_I2P_DAEMON_IP}" + I2P_DAEMON_PORT: "7656" + networks: + default: + ipv4_address: $APP_BITCOIN_KNOTS_IP + + bitcoind: + image: retropexx/bitcoind:v25.1@sha256:4091eb570744d24f4234a0efc6d8b6aaf7de1c86d1a6e3077525d59bbd98336e + command: "${APP_BITCOIN_KNOTS_COMMAND}" + restart: unless-stopped + stop_grace_period: 15m30s + volumes: + - "${APP_BITCOIN_KNOTS_DATA_DIR}:/data/.bitcoin" + ports: + - "${APP_BITCOIN_KNOTS_P2P_PORT}:${APP_BITCOIN_KNOTS_INTERNAL_P2P_PORT}" + - "${APP_BITCOIN_KNOTS_RPC_PORT}:${APP_BITCOIN_KNOTS_INTERNAL_RPC_PORT}" + networks: + default: + ipv4_address: $APP_BITCOIN_KNOTS_NODE_IP + + tor: + image: getumbrel/tor:0.4.7.8@sha256:2ace83f22501f58857fa9b403009f595137fa2e7986c4fda79d82a8119072b6a + user: "1000:1000" + restart: on-failure + volumes: + - ${APP_DATA_DIR}/torrc:/etc/tor/torrc:ro + - ${TOR_DATA_DIR}:/data + environment: + HOME: "/tmp" + networks: + default: + ipv4_address: "${APP_BITCOIN_KNOTS_TOR_PROXY_IP}" + + i2pd_daemon: + image: purplei2p/i2pd:release-2.44.0@sha256:d154a599793c393cf9c91f8549ba7ece0bb40e5728e1813aa6dd4c210aa606f6 + user: "root" + command: --sam.enabled=true --sam.address=0.0.0.0 --sam.port=7656 --loglevel=error + restart: on-failure + volumes: + - ${APP_DATA_DIR}/data/i2pd:/home/i2pd/data + networks: + default: + ipv4_address: "${APP_BITCOIN_KNOTS_I2P_DAEMON_IP}" diff --git a/bitcoin-knots/exports.sh b/bitcoin-knots/exports.sh new file mode 100644 index 0000000..5cd15cc --- /dev/null +++ b/bitcoin-knots/exports.sh @@ -0,0 +1,158 @@ +export APP_BITCOIN_KNOTS_IP="10.21.22.5" +export APP_BITCOIN_KNOTS_NODE_IP="10.21.21.7" +export APP_BITCOIN_KNOTS_TOR_PROXY_IP="10.21.22.12" +export APP_BITCOIN_KNOTS_I2P_DAEMON_IP="10.21.22.13" + +export APP_BITCOIN_KNOTS_DATA_DIR="${EXPORTS_APP_DIR}/data/bitcoin" +export APP_BITCOIN_KNOTS_RPC_PORT="9332" +export APP_BITCOIN_KNOTS_P2P_PORT="9333" +export APP_BITCOIN_KNOTS_TOR_PORT="9334" +export APP_BITCOIN_KNOTS_ZMQ_RAWBLOCK_PORT="48332" +export APP_BITCOIN_KNOTS_ZMQ_RAWTX_PORT="48333" +export APP_BITCOIN_KNOTS_ZMQ_HASHBLOCK_PORT="48334" +export APP_BITCOIN_KNOTS_ZMQ_SEQUENCE_PORT="48335" +export APP_BITCOIN_KNOTS_INTERNAL_RPC_PORT="8332" +export APP_BITCOIN_KNOTS_INTERNAL_P2P_PORT="8333" +export APP_BITCOIN_KNOTS_INTERNAL_tor_PORT="8334" + +BITCOIN_CHAIN="main" +BITCOIN_ENV_FILE="${EXPORTS_APP_DIR}/.env" + +{ + BITCOIN_APP_CONFIG_FILE="${EXPORTS_APP_DIR}/data/app/bitcoin-config.json" + if [[ -f "${BITCOIN_APP_CONFIG_FILE}" ]] + then + bitcoin_app_network=$(jq -r '.network' "${BITCOIN_APP_CONFIG_FILE}") + case $bitcoin_app_network in + "main") + BITCOIN_NETWORK="mainnet";; + "test") + BITCOIN_NETWORK="testnet";; + "signet") + BITCOIN_NETWORK="signet";; + "regtest") + BITCOIN_NETWORK="regtest";; + esac + fi +} > /dev/null || true + +if [[ ! -f "${BITCOIN_ENV_FILE}" ]]; then + if [[ -z "${BITCOIN_NETWORK}" ]]; then + BITCOIN_NETWORK="mainnet" + fi + + if [[ -z ${BITCOIN_RPC_USER+x} ]] || [[ -z ${BITCOIN_RPC_PASS+x} ]] || [[ -z ${BITCOIN_RPC_AUTH+x} ]]; then + BITCOIN_RPC_USER="umbrel" + BITCOIN_RPC_DETAILS=$("${EXPORTS_APP_DIR}/scripts/rpcauth.py" "${BITCOIN_RPC_USER}") + BITCOIN_RPC_PASS=$(echo "$BITCOIN_RPC_DETAILS" | tail -1) + BITCOIN_RPC_AUTH=$(echo "$BITCOIN_RPC_DETAILS" | head -2 | tail -1 | sed -e "s/^rpcauth=//") + fi + + echo "export APP_BITCOIN_KNOTS_NETWORK='${BITCOIN_NETWORK}'" > "${BITCOIN_ENV_FILE}" + echo "export APP_BITCOIN_KNOTS_RPC_USER='${BITCOIN_RPC_USER}'" >> "${BITCOIN_ENV_FILE}" + echo "export APP_BITCOIN_KNOTS_RPC_PASS='${BITCOIN_RPC_PASS}'" >> "${BITCOIN_ENV_FILE}" + echo "export APP_BITCOIN_KNOTS_RPC_AUTH='${BITCOIN_RPC_AUTH}'" >> "${BITCOIN_ENV_FILE}" +fi + +. "${BITCOIN_ENV_FILE}" + +# Make sure we don't persist the original value in .env if we have a more recent +# value from the app config +{ + if [[ ! -z ${BITCOIN_NETWORK+x} ]] && [[ "${BITCOIN_NETWORK}" ]] && [[ "${APP_BITCOIN_KNOTS_NETWORK}" ]] + then + APP_BITCOIN_KNOTS_NETWORK="${BITCOIN_NETWORK}" + fi +} > /dev/null || true + +if [[ "${APP_BITCOIN_KNOTS_NETWORK}" == "mainnet" ]]; then + BITCOIN_CHAIN="main" +elif [[ "${APP_BITCOIN_KNOTS_NETWORK}" == "testnet" ]]; then + BITCOIN_CHAIN="test" + # export APP_BITCOIN_RPC_PORT="18332" + # export APP_BITCOIN_P2P_PORT="18333" + # export APP_BITCOIN_TOR_PORT="18334" +elif [[ "${APP_BITCOIN_KNOTS_NETWORK}" == "signet" ]]; then + BITCOIN_CHAIN="signet" + # export APP_BITCOIN_RPC_PORT="38332" + # export APP_BITCOIN_P2P_PORT="38333" + # export APP_BITCOIN_TOR_PORT="38334" +elif [[ "${APP_BITCOIN_KNOTS_NETWORK}" == "regtest" ]]; then + BITCOIN_CHAIN="regtest" + # export APP_BITCOIN_RPC_PORT="18443" + # export APP_BITCOIN_P2P_PORT="18444" + # export APP_BITCOIN_TOR_PORT="18445" +else + echo "Warning (${EXPORTS_APP_ID}): Bitcoin Network '${APP_BITCOIN_KNOTS_NETWORK}' is not supported" +fi + +export BITCOIN_KNOTS_DEFAULT_NETWORK="${BITCOIN_CHAIN}" + +BIN_ARGS=() +# Commenting out options that are replaced by generated config file. We should migrate all these over in a future update. +# BIN_ARGS+=( "-chain=${BITCOIN_CHAIN}" ) +# BIN_ARGS+=( "-proxy=${TOR_PROXY_IP}:${TOR_PROXY_PORT}" ) +# BIN_ARGS+=( "-listen" ) +# BIN_ARGS+=( "-bind=0.0.0.0:${APP_BITCOIN_TOR_PORT}=onion" ) +# BIN_ARGS+=( "-bind=${APP_BITCOIN_NODE_IP}" ) +# BIN_ARGS+=( "-port=${APP_BITCOIN_P2P_PORT}" ) +# BIN_ARGS+=( "-rpcport=${APP_BITCOIN_RPC_PORT}" ) +BIN_ARGS+=( "-port=8333" ) +BIN_ARGS+=( "-rpcport=8332" ) +BIN_ARGS+=( "-rpcbind=${APP_BITCOIN_KNOTS_NODE_IP}" ) +BIN_ARGS+=( "-rpcbind=127.0.0.1" ) +BIN_ARGS+=( "-rpcallowip=${NETWORK_IP}/16" ) +BIN_ARGS+=( "-rpcallowip=127.0.0.1" ) +BIN_ARGS+=( "-rpcauth=\"${APP_BITCOIN_KNOTS_RPC_AUTH}\"" ) +BIN_ARGS+=( "-zmqpubrawblock=tcp://0.0.0.0:${APP_BITCOIN_KNOTS_ZMQ_RAWBLOCK_PORT}" ) +BIN_ARGS+=( "-zmqpubrawtx=tcp://0.0.0.0:${APP_BITCOIN_KNOTS_ZMQ_RAWTX_PORT}" ) +BIN_ARGS+=( "-zmqpubhashblock=tcp://0.0.0.0:${APP_BITCOIN_KNOTS_ZMQ_HASHBLOCK_PORT}" ) +BIN_ARGS+=( "-zmqpubsequence=tcp://0.0.0.0:${APP_BITCOIN_KNOTS_ZMQ_SEQUENCE_PORT}" ) +# BIN_ARGS+=( "-txindex=1" ) +# BIN_ARGS+=( "-blockfilterindex=1" ) +# BIN_ARGS+=( "-peerbloomfilters=1" ) +# BIN_ARGS+=( "-peerblockfilters=1" ) +# BIN_ARGS+=( "-rpcworkqueue=128" ) + +export APP_BITCOIN_KNOTS_COMMAND=$(IFS=" "; echo "${BIN_ARGS[@]}") + +# echo "${APP_BITCOIN_KNOTS_COMMAND}" + +rpc_hidden_service_file="${EXPORTS_TOR_DATA_DIR}/app-${EXPORTS_APP_ID}-rpc/hostname" +p2p_hidden_service_file="${EXPORTS_TOR_DATA_DIR}/app-${EXPORTS_APP_ID}-p2p/hostname" +export APP_BITCOIN_KNOTS_RPC_HIDDEN_SERVICE="$(cat "${rpc_hidden_service_file}" 2>/dev/null || echo "notyetset.onion")" +export APP_BITCOIN_KNOTS_P2P_HIDDEN_SERVICE="$(cat "${p2p_hidden_service_file}" 2>/dev/null || echo "notyetset.onion")" + +# electrs compatible network param +export APP_BITCOIN_KNOTS_NETWORK_ELECTRS=$APP_BITCOIN_KNOTS_NETWORK +if [[ "${APP_BITCOIN_KNOTS_NETWORK_ELECTRS}" = "mainnet" ]]; then + APP_BITCOIN_KNOTS_NETWORK_ELECTRS="bitcoin" +fi + +# We do not need this legacy bitcoin app logic because the Bitcoin Knots app was forked after advanced settings were introduced + +# { +# # Migrate settings for app updates differently to fresh installs +# BITCOIN_DATA_DIR="${EXPORTS_APP_DIR}/data/bitcoin" +# IS_POST_ADVANCED_SETTINGS_INSTALL_FILE_PATH="${EXPORTS_APP_DIR}/data/app/IS_POST_ADVANCED_SETTINGS_INSTALL" + +# # If no blocks directory exists, we write out a file to indicate that this is a fresh install. +# # This gets around the issue of the pre-start hook starting up the bitcoind container early for Tor HS creation +# # and creating the blocks directory. +# if [[ ! -d "${BITCOIN_DATA_DIR}/blocks" ]] && [[ ! -d "${BITCOIN_DATA_DIR}/testnet3/blocks" ]] && [[ ! -d "${BITCOIN_DATA_DIR}/regtest/blocks" ]] +# then +# touch "${IS_POST_ADVANCED_SETTINGS_INSTALL_FILE_PATH}" +# fi + +# APP_CONFIG_EXISTS="false" +# if [[ -f "${EXPORTS_APP_DIR}/data/app/bitcoin-config.json" ]] +# then +# APP_CONFIG_EXISTS="true" +# fi + +# if [[ ! -f "${IS_POST_ADVANCED_SETTINGS_INSTALL_FILE_PATH}" ]] && [[ "${APP_CONFIG_EXISTS}" = "false" ]] +# then +# # This app is not a fresh install, it's being updated, so preserve existing clearnet over Tor setting +# export BITCOIN_INITIALIZE_WITH_CLEARNET_OVER_TOR="true" +# fi +# } || true \ No newline at end of file diff --git a/bitcoin-knots/hooks/pre-start b/bitcoin-knots/hooks/pre-start new file mode 100755 index 0000000..5f4da55 --- /dev/null +++ b/bitcoin-knots/hooks/pre-start @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# Delay booting Bitcoin until the RPC and P2P Tor Hidden Services are ready + +HIDDEN_SERVICE_FILE="${TOR_DATA_DIR}/app-${APP_ID}-rpc/hostname" + +if [[ -f "${HIDDEN_SERVICE_FILE}" ]]; then + exit +fi + +"${UMBREL_ROOT}/scripts/app" compose "${APP_ID}" up --detach bitcoind +"${UMBREL_ROOT}/scripts/app" compose "${APP_ID}" up --detach tor + +echo "App: ${APP_ID} - Generating Tor Hidden Service..." + +for attempt in $(seq 1 100); do + if [[ -f "${HIDDEN_SERVICE_FILE}" ]]; then + echo "App: ${APP_ID} - Hidden service file created successfully!" + break + fi + sleep 0.1 +done + +if [[ ! -f "${HIDDEN_SERVICE_FILE}" ]]; then + echo "App: ${APP_ID} - Hidden service file wasn't created" +fi \ No newline at end of file diff --git a/bitcoin-knots/scripts/rpcauth.py b/bitcoin-knots/scripts/rpcauth.py new file mode 100755 index 0000000..b14c801 --- /dev/null +++ b/bitcoin-knots/scripts/rpcauth.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from argparse import ArgumentParser +from base64 import urlsafe_b64encode +from binascii import hexlify +from getpass import getpass +from os import urandom + +import hmac + +def generate_salt(size): + """Create size byte hex salt""" + return hexlify(urandom(size)).decode() + +def generate_password(): + """Create 32 byte b64 password""" + return urlsafe_b64encode(urandom(32)).decode('utf-8') + +def password_to_hmac(salt, password): + m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), 'SHA256') + return m.hexdigest() + +def main(): + parser = ArgumentParser(description='Create login credentials for a JSON-RPC user') + parser.add_argument('username', help='the username for authentication') + parser.add_argument('password', help='leave empty to generate a random password or specify "-" to prompt for password', nargs='?') + args = parser.parse_args() + + if not args.password: + args.password = generate_password() + elif args.password == '-': + args.password = getpass() + + # Create 16 byte hex salt + salt = generate_salt(16) + password_hmac = password_to_hmac(salt, args.password) + + print('String to be appended to bitcoin.conf:') + print('rpcauth={0}:{1}${2}'.format(args.username, salt, password_hmac)) + print('Your password:\n{0}'.format(args.password)) + +if __name__ == '__main__': + main() diff --git a/bitcoin-knots/torrc.template b/bitcoin-knots/torrc.template new file mode 100644 index 0000000..002f411 --- /dev/null +++ b/bitcoin-knots/torrc.template @@ -0,0 +1,13 @@ +SocksPort 0.0.0.0:9050 +ControlPort 0.0.0.0:9051 +CookieAuthentication 1 +CookieAuthFileGroupReadable 1 +HashedControlPassword 16:39AF5EEFA4FC1D986022FDFB13663669FE50FB6DE9A3B4FE4FC7D82010 # moneyprintergobrrr + +# Bitcoin Core P2P Hidden Service +HiddenServiceDir /data/app-$APP_ID-p2p +HiddenServicePort $APP_BITCOIN_P2P_PORT $APP_BITCOIN_NODE_IP:$APP_BITCOIN_TOR_PORT + +# Bitcoin Core RPC Hidden Service +HiddenServiceDir /data/app-$APP_ID-rpc +HiddenServicePort $APP_BITCOIN_RPC_PORT $APP_BITCOIN_NODE_IP:$APP_BITCOIN_RPC_PORT \ No newline at end of file diff --git a/bitcoin-knots/umbrel-app.yml b/bitcoin-knots/umbrel-app.yml new file mode 100644 index 0000000..bb73e3b --- /dev/null +++ b/bitcoin-knots/umbrel-app.yml @@ -0,0 +1,31 @@ +manifestVersion: 1.1 +id: bitcoin-knots +category: bitcoin +name: Bitcoin Knots +version: "25.1" +tagline: Run your personal node powered by Bitcoin Knots +description: >- + Take control of your digital sovereignty by running a Bitcoin node that aligns with your needs and preferences. + Independently store and validate every single Bitcoin transaction with Bitcoin Knots. + + + ⚠️ Bitcoin Knots does not yet automatically integrate with other apps in the Bitcoin ecosystem on Umbrel. + However, in a future umbrelOS update, users will be able to choose which Bitcoin Node app they want to use to connect with other apps. + + + Powered by Bitcoin Knots: https://bitcoinknots.org/ +developer: Léo Haf +website: https://bitcoinknots.org +dependencies: [] +repo: https://github.com/bitcoinknots/bitcoin.git +support: https://github.com/bitcoinknots/bitcoin/issues +port: 2105 +gallery: + - 1.jpg + - 2.jpg + - 3.jpg +path: "" +defaultPassword: "" +releaseNotes: "" +submitter: Léo Haf +submission: https://github.com/getumbrel/umbrel-apps/pull/953