#!/usr/bin/env bash # docker-finance | modern accounting for the power-user # # Copyright (C) 2025 Aaron Fiore (Founder, Evergreen Crypto LLC) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # NOTES: # # This plugin provides a rudimentary clone/pull/build process that # adheres to the expectations from the container-side bitcoin plugin. # # The only requirement for this plugin is that build dependencies # are installed on your existing/running image (which is assumed to # have also been built with the `root` module enabled): # # 1. Uncomment plugin's image build requirements: # # $ dfi edit type=build # # 2. Build/re-build your image with `root` module: # # $ dfi build type=default # # If you do bitcoin development on your client (host) and prefer to # use your own build process, you can skip the above requirements and, # instead, ensure the following: # # 1. Your bitcoin repository is dropped into your client-side shared # directory: # # $DOCKER_FINANCE_CLIENT_SHARED # # The expected path by the container-side plugin will be in your # container-side shared directory: # # "${DOCKER_FINANCE_CONTAINER_SHARED}"/bitcoin # # To see or edit your shared client/container path prior to # running this (or the container's) plugin, run the following: # # $ dfi edit type=env # # 2. Bitcoin's libraries are built as a shared (when possible) and # should continue to remain within the repo's build directory, # prior to running the container-side `root` bitcoin plugin. # # # "Libraries" # [ -z "$DOCKER_FINANCE_CLIENT_REPO" ] && exit 1 source "${DOCKER_FINANCE_CLIENT_REPO}/client/src/docker/lib/lib_docker.bash" || exit 1 [[ -z "$global_platform" || -z "$global_arg_delim_1" || -z "$global_user" || -z "$global_tag" ]] && lib_utils::die_fatal instance="${global_platform}${global_arg_delim_1}${global_user}:${global_tag}" # Initialize "constructor" # NOTE: "constructor" only needed if calling library directly lib_docker::docker "$instance" || lib_utils::die_fatal # # Implementation # # WARNING: *MUST* be synced with image (assuming Arch Linux finance image) declare -r plugin_bitcoin_deps=("boost" "capnproto" "cmake" "db") # WARNING: *MUST* be synced with container's plugin implementation [ -z "$DOCKER_FINANCE_CONTAINER_SHARED" ] && lib_utils::die_fatal declare -r plugin_bitcoin_path="${DOCKER_FINANCE_CONTAINER_SHARED}/bitcoin" function bitcoin::__parse_args() { [ -z "$global_usage" ] && lib_utils::die_fatal [ -z "$global_arg_delim_1" ] && lib_utils::die_fatal [ -z "$global_arg_delim_2" ] && lib_utils::die_fatal local -r _default_arg_remote="https://github.com/bitcoin/bitcoin" local -r _default_arg_branch="master" local -r _default_arg_cmake="-DBUILD_BITCOIN_BIN=OFF -DBUILD_CLI=OFF -DBUILD_DAEMON=OFF -DBUILD_TESTS=OFF -DBUILD_UTIL=OFF -DBUILD_WALLET_TOOL=OFF -DINSTALL_MAN=OFF -DSECP256K1_BUILD_TESTS=OFF -DSECP256K1_BUILD_EXHAUSTIVE_TESTS=OFF -DBUILD_KERNEL_LIB=ON -DBUILD_SHARED_LIBS=ON" # Re-seat global usage local _global_usage _global_usage="$global_usage plugins repo${global_arg_delim_1}$(basename $0)" declare -r _global_usage local -r _usage=" \e[32mDescription:\e[0m Build bitcoin libraries for \`root\` container-side plugin. \e[32mUsage:\e[0m $ $_global_usage [remote${global_arg_delim_2}] [branch${global_arg_delim_2}] [cmake${global_arg_delim_2}] \e[32mArguments:\e[0m Git remote to get; default: remote${global_arg_delim_2}\"$_default_arg_remote\" Git branch to build from; default: branch${global_arg_delim_2}\"$_default_arg_branch\" CMake build options; default: cmake${global_arg_delim_2}\"$_default_arg_cmake\" \e[32mExamples:\e[0m \e[37;2m# Get from default\e[0m $ $_global_usage get \e[37;2m# Get from given remote 'https://my.custom.remote/bitcoin'\e[0m $ $_global_usage get remote${global_arg_delim_2}\"https://my.custom.remote/bitcoin\" \e[37;2m# Build default branch\e[0m $ $_global_usage build \e[37;2m# Checkout and build given branch\e[0m $ $_global_usage build branch=\"my_dev\" \e[37;2m# Checkout and build given commit\e[0m $ $_global_usage build branch=\"b30262dcaa28c40a0a5072847b7194b3db203160\" \e[37;2m# Build given branch with custom CMake options\e[0m $ $_global_usage build branch=\"my_dev\" cmake${global_arg_delim_2}\"-DBUILD_BITCOIN_BIN=ON -DBUILD_CLI=ON -DBUILD_DAEMON=ON -DBUILD_TESTS=ON -DBUILD_UTIL=ON -DBUILD_WALLET_TOOL=ON -DINSTALL_MAN=ON -DSECP256K1_BUILD_TESTS=ON -DSECP256K1_BUILD_EXHAUSTIVE_TESTS=ON -DBUILD_KERNEL_LIB=ON -DBUILD_SHARED_LIBS=ON\" \e[37;2m# Clean entire build directory\e[0m $ $_global_usage clean " [ $# -eq 0 ] && lib_utils::die_usage "$_usage" [[ ! "$1" =~ ^get$|^build$|^clean$ ]] \ && lib_utils::die_usage "$_usage" \ || declare -gr plugin_arg_cmd="$1" for _arg in "${@:2}"; do [[ ! "$_arg" =~ ^remote${global_arg_delim_2} ]] \ && [[ ! "$_arg" =~ ^branch${global_arg_delim_2} ]] \ && [[ ! "$_arg" =~ ^cmake${global_arg_delim_2} ]] \ && lib_utils::die_usage "$_usage" done for _arg in "${@:2}"; do local _key="${_arg%${global_arg_delim_2}*}" local _len="$((${#_key} + 1))" if [[ "$_key" =~ ^remote$ ]]; then local _arg_remote="${_arg:${_len}}" [ -z "$_arg_remote" ] && lib_utils::die_usage "$_usage" fi if [[ "$_key" =~ ^branch$ ]]; then local _arg_branch="${_arg:${_len}}" [ -z "$_arg_branch" ] && lib_utils::die_usage "$_usage" fi if [[ "$_key" =~ ^cmake$ ]]; then local _arg_cmake="${_arg:${_len}}" [ -z "$_arg_cmake" ] && lib_utils::die_usage "$_usage" fi done # Arg: get if [[ "$plugin_arg_cmd" =~ ^get$ ]]; then if [[ ! -z "$_arg_branch" || ! -z "$_arg_cmake" ]]; then lib_utils::die_usage "$_usage" fi # Arg: remote if [ -z "$_arg_remote" ]; then _arg_remote="$_default_arg_remote" fi declare -gr plugin_arg_remote="$_arg_remote" lib_utils::print_info "Using remote: ${_arg_remote}" fi # Arg: build if [[ "$plugin_arg_cmd" =~ ^build$ ]]; then if [[ ! -z "$_arg_remote" ]]; then lib_utils::die_usage "$_usage" fi # Arg: branch if [ -z "$_arg_branch" ]; then _arg_branch="$_default_arg_branch" fi declare -gr plugin_arg_branch="$_arg_branch" lib_utils::print_info "Using branch: ${plugin_arg_branch}" # Arg: cmake if [ -z "$_arg_cmake" ]; then _arg_cmake="$_default_arg_cmake" fi declare -gr plugin_arg_cmake="$_arg_cmake" lib_utils::print_info "Using CMake options: ${plugin_arg_cmake}" fi # Arg: clean if [[ "$plugin_arg_cmd" =~ ^clean$ ]]; then if [[ ! -z "$_arg_remote" || ! -z "$_arg_branch" || ! -z "$_arg_cmake" ]]; then lib_utils::die_usage "$_usage" fi fi } function bitcoin::__getter() { [ -z "$plugin_arg_remote" ] && lib_utils::die_fatal [ -z "$plugin_bitcoin_path" ] && lib_utils::die_fatal lib_docker::exec "if [ ! -d $plugin_bitcoin_path ]; then git clone $plugin_arg_remote $plugin_bitcoin_path ; fi" \ || lib_utils::die_fatal "Could not clone '${plugin_arg_remote}' to '${plugin_bitcoin_path}'" lib_docker::exec "pushd $plugin_bitcoin_path 1>/dev/null && git pull $plugin_arg_remote && popd 1>/dev/null" \ || lib_utils::die_fatal "Could not pull '${plugin_arg_remote}' to '${plugin_bitcoin_path}'" } function bitcoin::__builder() { # # Ensure dependencies # local -r _no_deps="Build dependencies not found! Did you uncomment them with: \t\$ dfi $instance edit type=build and then re-build your image with: \t\$ dfi $instance build type=" # NOTE: *MUST* be synced with end-user Dockerfile (`edit type=build`) # TODO: not ideal, would prefer runtime dependency pulling but that has drawbacks as well [ -z "${plugin_bitcoin_deps[*]}" ] && lib_utils::die_fatal lib_docker::exec "pacman -Q ${plugin_bitcoin_deps[*]}" | grep "^error: package" \ && lib_utils::die_fatal "$_no_deps" # # Execute build # [ -z "$plugin_arg_cmake" ] && lib_utils::die_fatal [ -z "$plugin_bitcoin_path" ] && lib_utils::die_fatal lib_docker::exec "pushd $plugin_bitcoin_path 1>/dev/null && git checkout $plugin_arg_branch && popd 1>/dev/null" \ || lib_utils::die_fatal "Could not checkout '${plugin_arg_branch}'" lib_docker::exec "cmake $plugin_arg_cmake -B ${plugin_bitcoin_path}/build $plugin_bitcoin_path" \ || lib_utils::die_fatal "Could not prepare build with given options" lib_docker::exec "cmake --build ${plugin_bitcoin_path}/build -j$(nproc)" \ || lib_utils::die_fatal "Could not build" lib_utils::print_info "$(lib_docker::exec "find ${plugin_bitcoin_path} -name libbitcoinkernel.so")" } function bitcoin::__cleaner() { [ -z "$plugin_bitcoin_path" ] && lib_utils::die_fatal local -r _build="${plugin_bitcoin_path}/build" lib_utils::print_info "Removing '${_build}'" lib_docker::exec "rm -fr $_build" || lib_utils::die_fatal "Could not delete= '${_build}'" } # # Facade # function bitcoin::get() { lib_utils::print_info "Getting repository" bitcoin::__getter } function bitcoin::build() { lib_utils::print_info "Building repository" bitcoin::__builder } function bitcoin::clean() { lib_utils::print_info "Cleaning repository" bitcoin::__cleaner } function main() { bitcoin::__parse_args "$@" # Ensure running instance local -r _no_instance="Did you start a running instance? Run the following in a separate shell: \t$ dfi $instance up" lib_docker::exec "" || lib_utils::die_fatal "$_no_instance" [ -z "$plugin_arg_cmd" ] && lib_utils::die_fatal case "$plugin_arg_cmd" in get) bitcoin::get ;; build) bitcoin::build ;; clean) bitcoin::clean ;; *) lib_utils::die_fatal "Not implemented" ;; esac } main "$@" # vim: sw=2 sts=2 si ai et