mirror of
https://github.com/JoinMarket-Org/joinmarket-clientserver.git
synced 2026-08-13 12:33:40 +02:00
654 lines
16 KiB
Bash
Executable file
654 lines
16 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
cd "$(dirname "$0")" || exit
|
|
|
|
check_exists()
|
|
{
|
|
command -v "$1" > /dev/null
|
|
}
|
|
|
|
# This may be overriden by --python/-p option.
|
|
python="python3"
|
|
|
|
# This is needed for systems where GNU is not the default make, like FreeBSD.
|
|
if check_exists gmake; then
|
|
make="gmake"
|
|
else
|
|
make="make"
|
|
fi
|
|
|
|
num_cores()
|
|
{
|
|
${python} -c 'import multiprocessing as mp; print(mp.cpu_count())'
|
|
}
|
|
|
|
sha256_verify ()
|
|
{
|
|
if [[ "$(uname)" == "Darwin" ]]; then
|
|
shasum -a 256 -c <<<"$1 $2"
|
|
return "$?"
|
|
elif [[ "$(uname)" == "FreeBSD" ]]; then
|
|
sha256 -c "$1" "$2"
|
|
return "$?"
|
|
else
|
|
sha256sum -c <<<"$1 $2"
|
|
return "$?"
|
|
fi
|
|
}
|
|
|
|
# http_get url filename
|
|
http_get ()
|
|
{
|
|
if check_exists curl; then
|
|
curl --retry 5 -L "$1" -o "$2"
|
|
elif check_exists wget; then
|
|
wget "$1" -O "$2"
|
|
else
|
|
echo "Neither curl nor wget present; please install one of them using your OS package manager."
|
|
kill $$
|
|
fi
|
|
}
|
|
|
|
gpg_verify ()
|
|
{
|
|
if [[ $no_gpg_validation != 1 ]]; then
|
|
if ! check_exists gpg; then
|
|
echo "GPG not installed, cannot verify release signatures; install gpg or use --no-gpg-validation."
|
|
kill $$
|
|
fi
|
|
gpg --import "$1"
|
|
gpg --verify "$2" || kill $$
|
|
fi
|
|
}
|
|
|
|
deps_install ()
|
|
{
|
|
debian_deps=( \
|
|
'curl' \
|
|
'build-essential' \
|
|
'automake' \
|
|
'autoconf' \
|
|
'pkg-config' \
|
|
'libtool' \
|
|
'python3-dev' \
|
|
'python3-pip' \
|
|
'python3-setuptools' \
|
|
'python3-venv' \
|
|
'libffi-dev' \
|
|
'libffi8' \
|
|
'libltdl-dev' )
|
|
|
|
if [ "$with_sudo" == 1 ]; then debian_deps+=("sudo"); fi
|
|
|
|
darwin_deps=( \
|
|
'automake' \
|
|
'libtool' )
|
|
|
|
if ! is_python3; then
|
|
echo "Python 2 is no longer supported. Please use a compatible Python 3 version."
|
|
return 1
|
|
fi
|
|
|
|
if [[ ${use_os_deps_check} != '1' ]]; then
|
|
echo "Checking OS package manager's dependencies disabled. Trying to build."
|
|
return 0
|
|
elif [[ ${install_os} == 'debian' ]]; then
|
|
deb_deps_install "${debian_deps[@]}"
|
|
return "$?"
|
|
elif [[ ${install_os} == 'darwin' ]]; then
|
|
dar_deps_install "${darwin_deps[@]}"
|
|
return "$?"
|
|
else
|
|
echo "OS can not be determined. Trying to build."
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
tor_deps_install ()
|
|
{
|
|
debian_deps=( \
|
|
'libevent-dev' \
|
|
'libssl-dev' \
|
|
'zlib1g-dev' )
|
|
|
|
darwin_deps=( \
|
|
'libevent' \
|
|
'zlib' )
|
|
|
|
if [[ ${use_os_deps_check} != '1' ]]; then
|
|
return 0
|
|
elif [[ ${install_os} == 'debian' ]]; then
|
|
deb_deps_install "${debian_deps[@]}"
|
|
return "$?"
|
|
elif [[ ${install_os} == 'darwin' ]]; then
|
|
dar_deps_install "${darwin_deps[@]}"
|
|
return "$?"
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
deb_deps_check ()
|
|
{
|
|
LANG=C apt-cache policy "${deb_deps[@]}" | grep "Installed.*none"
|
|
}
|
|
|
|
deb_deps_install ()
|
|
{
|
|
deb_deps=( "${@}" )
|
|
if deb_deps_check; then
|
|
clear
|
|
sudo_command=''
|
|
if [ "$with_sudo" == 1 ]; then
|
|
echo "
|
|
sudo password required to run :
|
|
|
|
\`apt-get install ${deb_deps[*]}\`
|
|
"
|
|
sudo_command="sudo"
|
|
fi
|
|
|
|
if ! $sudo_command apt-get install -y --no-install-recommends "${deb_deps[@]}"; then
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
dar_deps_install ()
|
|
{
|
|
dar_deps=( "${@}" )
|
|
if ! brew install "${dar_deps[@]}"; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_skip_build ()
|
|
{
|
|
if [[ ${reinstall} == false ]] && [[ -d "$1" ]]; then
|
|
read -r -n 1 -p "Directory ${1} exists. Remove and recreate? (y/N) " q
|
|
echo ""
|
|
if [[ "${q}" =~ Y|y ]]; then
|
|
rm -rf "./${1}"
|
|
mkdir -p "./${1}"
|
|
return 1
|
|
else
|
|
echo "skipping ${1}..."
|
|
return 0
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
upgrade_setuptools ()
|
|
{
|
|
pip install --upgrade pip
|
|
pip install --upgrade setuptools
|
|
}
|
|
|
|
venv_setup ()
|
|
{
|
|
if check_skip_build 'jmvenv'; then
|
|
return 0
|
|
else
|
|
reinstall='true'
|
|
fi
|
|
"${python}" -m venv "${jm_source}/jmvenv" || return 1
|
|
# shellcheck source=/dev/null
|
|
source "${jm_source}/jmvenv/bin/activate" || return 1
|
|
upgrade_setuptools
|
|
deactivate
|
|
}
|
|
|
|
dep_get ()
|
|
{
|
|
pkg_name="$1" pkg_hash="$2" pkg_url="$3"
|
|
pkg_pubkeys="$4" pkg_sig="$5" pkg_hash_file="$6" pkg_hash_file_sig="$7"
|
|
|
|
pushd cache || return 1
|
|
if [ ! -f "${pkg_name}" ] || ! sha256_verify "${pkg_hash}" "${pkg_name}"; then
|
|
http_get "${pkg_url}/${pkg_name}" "${pkg_name}"
|
|
fi
|
|
if ! sha256_verify "${pkg_hash}" "${pkg_name}"; then
|
|
return 1
|
|
fi
|
|
if [[ -n "${pkg_hash_file}" ]]; then
|
|
http_get "${pkg_url}/${pkg_hash_file}" "${pkg_hash_file}"
|
|
if [[ -n "${pkg_hash_file_sig}" ]]; then
|
|
http_get "${pkg_url}/${pkg_hash_file_sig}" "${pkg_hash_file_sig}"
|
|
gpg_verify "../../pubkeys/third-party/${pkg_pubkeys}" "${pkg_hash_file_sig}"
|
|
fi
|
|
if ! grep -qs "${pkg_hash}" "${pkg_hash_file}"; then
|
|
echo "Hash mismatch, ${pkg_hash} not in ${pkg_url}/${pkg_hash_file}!"
|
|
return 1
|
|
fi
|
|
fi
|
|
if [[ -n "${pkg_sig}" ]]; then
|
|
http_get "${pkg_url}/${pkg_sig}" "${pkg_sig}"
|
|
gpg_verify "../../pubkeys/third-party/${pkg_pubkeys}" "${pkg_sig}"
|
|
fi
|
|
tar -xzf "${pkg_name}" -C ../
|
|
popd || return 1
|
|
}
|
|
|
|
libffi_autoreconf_patch ()
|
|
{
|
|
# autogen.sh is not happy when run from some directories, causing it
|
|
# to create an ltmain.sh file in our ${jm_root} directory. weird.
|
|
# https://github.com/meetecho/janus-gateway/issues/290#issuecomment-125160739
|
|
# https://github.com/meetecho/janus-gateway/commit/ac38cfdae7185f9061569b14809af4d4052da700
|
|
cat <<'EOF' > autoreconf.patch
|
|
18a19
|
|
> AC_CONFIG_AUX_DIR([.])
|
|
EOF
|
|
patch configure.ac autoreconf.patch
|
|
}
|
|
|
|
libffi_build ()
|
|
{
|
|
./autogen.sh
|
|
./configure --disable-docs --enable-shared --prefix="${jm_root}"
|
|
$make uninstall
|
|
$make
|
|
if ! $make check; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
libffi_install ()
|
|
{
|
|
libffi_version='libffi-3.4.6'
|
|
libffi_lib_tar="v3.4.6.tar.gz"
|
|
libffi_lib_sha='9ac790464c1eb2f5ab5809e978a1683e9393131aede72d1b0a0703771d3c6cda'
|
|
libffi_url="https://github.com/libffi/libffi/archive"
|
|
|
|
if check_skip_build "${libffi_version}"; then
|
|
return 0
|
|
fi
|
|
if ! dep_get "${libffi_lib_tar}" "${libffi_lib_sha}" "${libffi_url}"; then
|
|
return 1
|
|
fi
|
|
pushd "${libffi_version}" || return 1
|
|
if ! libffi_autoreconf_patch; then
|
|
return 1
|
|
fi
|
|
if libffi_build; then
|
|
$make install
|
|
else
|
|
return 1
|
|
fi
|
|
popd || return 1
|
|
}
|
|
|
|
libsecp256k1_build()
|
|
{
|
|
$make clean
|
|
./autogen.sh
|
|
./configure \
|
|
--enable-module-recovery \
|
|
--prefix "${jm_root}" \
|
|
--enable-experimental \
|
|
--enable-module-ecdh \
|
|
--enable-benchmark=no \
|
|
MAKE=$make
|
|
$make
|
|
if [[ $use_secp_check == '1' ]]; then
|
|
if ! $make check; then
|
|
return 1
|
|
fi
|
|
else
|
|
echo "Skipping libsecp256k1 tests."
|
|
fi
|
|
}
|
|
|
|
libsecp256k1_install()
|
|
{
|
|
secp256k1_version="0.5.0"
|
|
secp256k1_lib_tar="v$secp256k1_version.tar.gz"
|
|
secp256k1_lib_sha="07934fde88c677abbc4d42c36ef7ef8d3850cd0c065e4f976f66f4f97502c95a"
|
|
secp256k1_lib_url='https://github.com/bitcoin-core/secp256k1/archive/refs/tags'
|
|
if ! dep_get "${secp256k1_lib_tar}" "${secp256k1_lib_sha}" "${secp256k1_lib_url}"; then
|
|
return 1
|
|
fi
|
|
pushd "secp256k1-$secp256k1_version" || return 1
|
|
if libsecp256k1_build; then
|
|
$make install
|
|
else
|
|
return 1
|
|
fi
|
|
popd || return 1
|
|
}
|
|
|
|
libsodium_build ()
|
|
{
|
|
$make uninstall
|
|
$make distclean
|
|
./autogen.sh DO_NOT_UPDATE_CONFIG_SCRIPTS=1
|
|
./configure \
|
|
--enable-minimal \
|
|
--enable-shared \
|
|
--prefix="${jm_root}"
|
|
$make uninstall
|
|
$make
|
|
if ! $make check; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
libsodium_install ()
|
|
{
|
|
sodium_version='libsodium-1.0.20'
|
|
sodium_lib_tar="${sodium_version}.tar.gz"
|
|
sodium_lib_sha='ebb65ef6ca439333c2bb41a0c1990587288da07f6c7fd07cb3a18cc18d30ce19'
|
|
sodium_url='https://download.libsodium.org/libsodium/releases'
|
|
sodium_pubkeys='libsodium.asc'
|
|
|
|
if check_skip_build "${sodium_version}"; then
|
|
return 0
|
|
fi
|
|
if ! dep_get "${sodium_lib_tar}" "${sodium_lib_sha}" "${sodium_url}" "${sodium_pubkeys}" "${sodium_lib_tar}.sig"; then
|
|
return 1
|
|
fi
|
|
pushd "${sodium_version}" || return 1
|
|
if libsodium_build; then
|
|
$make install
|
|
else
|
|
return 1
|
|
fi
|
|
popd || return 1
|
|
}
|
|
|
|
tor_root ()
|
|
{
|
|
# jm_root will be empty for --docker-install,
|
|
# sys.prefix defaults to /usr/local
|
|
if [[ -n "$jm_root" ]]; then
|
|
echo "$jm_root"
|
|
else
|
|
echo "/usr/local"
|
|
fi
|
|
}
|
|
|
|
tor_build ()
|
|
{
|
|
$make uninstall
|
|
$make distclean
|
|
./configure \
|
|
--disable-system-torrc \
|
|
--disable-seccomp \
|
|
--disable-libscrypt \
|
|
--disable-module-relay \
|
|
--disable-lzma \
|
|
--disable-zstd \
|
|
--disable-asciidoc \
|
|
--disable-manpage \
|
|
--disable-html-manual \
|
|
--prefix="$(tor_root)"
|
|
$make
|
|
if ! $make check; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
tor_install ()
|
|
{
|
|
tor_version='tor-0.4.8.13'
|
|
tor_tar="${tor_version}.tar.gz"
|
|
tor_sha='9baf26c387a2820b3942da572146e6eb77c2bc66862af6297cd02a074e6fba28'
|
|
tor_url='https://dist.torproject.org'
|
|
tor_pubkeys='Tor.asc'
|
|
|
|
if ! dep_get "${tor_tar}" "${tor_sha}" "${tor_url}" "${tor_pubkeys}" "" "${tor_tar}.sha256sum" "${tor_tar}.sha256sum.asc"; then
|
|
return 1
|
|
fi
|
|
pushd "${tor_version}" || return 1
|
|
if tor_build; then
|
|
$make install
|
|
echo "# Default JoinMarket Tor configuration
|
|
Log warn stderr
|
|
SOCKSPort 9050 IsolateDestAddr IsolateDestPort
|
|
ControlPort 9051
|
|
CookieAuthentication 1
|
|
" > "$(tor_root)/etc/tor/torrc"
|
|
else
|
|
return 1
|
|
fi
|
|
popd || return 1
|
|
}
|
|
|
|
joinmarket_install ()
|
|
{
|
|
reqs='services'
|
|
|
|
if [[ ${with_qt} == "1" ]]; then
|
|
reqs='gui'
|
|
fi
|
|
if [[ ${develop} == "1" ]]; then
|
|
reqs+=',test'
|
|
fi
|
|
|
|
if [ "$with_jmvenv" == 1 ]; then pip_command=pip; else pip_command=pip3; fi
|
|
$pip_command install -e ".[${reqs}]" || return 1
|
|
|
|
if [[ ${with_qt} == "1" ]]; then
|
|
if [[ -d ~/.local/share/icons ]] && [[ -d ~/.local/share/applications ]]; then
|
|
echo "Installing XDG desktop entry"
|
|
cp -f "$(dirname "$0")/docs/images/joinmarket_logo.png" \
|
|
~/.local/share/icons/
|
|
sed "s/\\\$JMHOME/$(dirname "$(realpath "$0")" | sed 's/\//\\\//g')/" \
|
|
"$(dirname "$0")/joinmarket-qt.desktop" > \
|
|
~/.local/share/applications/joinmarket-qt.desktop
|
|
fi
|
|
fi
|
|
}
|
|
|
|
parse_flags ()
|
|
{
|
|
while :; do
|
|
case $1 in
|
|
--develop)
|
|
# editable install is currently always on
|
|
# option solely triggers test dependencies installation for now
|
|
develop='1'
|
|
;;
|
|
--disable-os-deps-check)
|
|
use_os_deps_check='0'
|
|
;;
|
|
--disable-secp-check)
|
|
use_secp_check='0'
|
|
;;
|
|
--no-gpg-validation)
|
|
no_gpg_validation='1'
|
|
;;
|
|
-p|--python)
|
|
if [[ "$2" ]]; then
|
|
python="$2"
|
|
shift
|
|
else
|
|
echo 'ERROR: "--python" requires a non-empty option argument.'
|
|
return 1
|
|
fi
|
|
;;
|
|
--python=?*)
|
|
python="${1#*=}"
|
|
;;
|
|
--python=)
|
|
echo 'ERROR: "--python" requires a non-empty option argument.'
|
|
return 1
|
|
;;
|
|
--with-local-tor)
|
|
build_local_tor='1'
|
|
;;
|
|
--with-qt)
|
|
with_qt='1'
|
|
;;
|
|
--without-qt)
|
|
with_qt='0'
|
|
;;
|
|
--docker-install)
|
|
with_sudo='0'
|
|
with_jmvenv='0'
|
|
;;
|
|
"")
|
|
break
|
|
;;
|
|
*)
|
|
if [[ $1 != '-h' ]] && [[ $1 != '--help' ]]; then
|
|
echo "Invalid option $1"
|
|
fi
|
|
echo "
|
|
Usage: ${0} [options]
|
|
|
|
Options:
|
|
|
|
--develop developer mode (install dependencies for testing)
|
|
--disable-os-deps-check skip OS package manager's dependency check
|
|
--disable-secp-check do not run libsecp256k1 tests (default is to run them)
|
|
--docker-install system wide install as root for minimal Docker installs
|
|
--no-gpg-validation disable GPG key validation for dependencies
|
|
--python, -p python version (only python3 versions are supported)
|
|
--with-local-tor build Tor locally and autostart when needed
|
|
--with-qt build the Qt GUI
|
|
--without-qt don't build the Qt GUI
|
|
"
|
|
return 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ ${with_qt} == '' ]]; then
|
|
read -r -n 1 -p "
|
|
INFO: Joinmarket-Qt for GUI Taker and Tumbler modes is available.
|
|
Install Qt dependencies (~160mb)? (y/N) "
|
|
echo ""
|
|
if [[ ${REPLY} =~ y|Y ]]; then
|
|
echo "Building Qt GUI"
|
|
with_qt='1'
|
|
else
|
|
echo "Not building Qt GUI"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
os_is_deb ()
|
|
{
|
|
( which apt-get && which dpkg-query ) 2>/dev/null 1>&2
|
|
}
|
|
|
|
os_is_dar ()
|
|
{
|
|
[[ "$(uname)" == "Darwin" ]]
|
|
}
|
|
|
|
is_python3 ()
|
|
{
|
|
if [[ ${python} == python3* ]]; then
|
|
return 0
|
|
fi
|
|
if [[ ${python} == python2* ]]; then
|
|
return 1
|
|
fi
|
|
${python} -c 'import sys; sys.exit(0) if sys.version_info >= (3,0) else sys.exit(1)'
|
|
}
|
|
|
|
install_get_os ()
|
|
{
|
|
if os_is_deb; then
|
|
echo 'debian'
|
|
elif os_is_dar; then
|
|
echo 'darwin'
|
|
else
|
|
echo 'unknown'
|
|
fi
|
|
}
|
|
|
|
main ()
|
|
{
|
|
# flags
|
|
build_local_tor=''
|
|
no_gpg_validation=''
|
|
use_os_deps_check='1'
|
|
use_secp_check='1'
|
|
with_qt=''
|
|
with_jmvenv='1'
|
|
with_sudo='1'
|
|
reinstall='false'
|
|
if ! parse_flags "${@}"; then
|
|
return 1
|
|
fi
|
|
|
|
jm_source="$PWD"
|
|
if [ "$with_jmvenv" == 1 ]; then
|
|
jm_root="${jm_source}/jmvenv"
|
|
export PKG_CONFIG_PATH="${jm_root}/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
|
export LD_LIBRARY_PATH="${jm_root}/lib:${LD_LIBRARY_PATH}"
|
|
export C_INCLUDE_PATH="${jm_root}/include:${C_INCLUDE_PATH}"
|
|
else
|
|
jm_root=""
|
|
fi
|
|
|
|
# os check
|
|
install_os="$( install_get_os )"
|
|
|
|
if ! deps_install; then
|
|
echo "Dependecies could not be installed. Exiting."
|
|
return 1
|
|
fi
|
|
|
|
MAKEFLAGS="-j $(num_cores)" && export MAKEFLAGS
|
|
|
|
if [ "$with_jmvenv" == 1 ]; then
|
|
if ! venv_setup; then
|
|
echo "Joinmarket Python virtual environment could not be setup. Exiting."
|
|
return 1
|
|
fi
|
|
# shellcheck source=/dev/null
|
|
source "${jm_root}/bin/activate"
|
|
else
|
|
upgrade_setuptools
|
|
fi
|
|
if [[ ${build_local_tor} == "1" ]]; then
|
|
if ! tor_deps_install; then
|
|
echo "Tor dependencies could not be installed. Exiting."
|
|
return 1
|
|
fi
|
|
fi
|
|
mkdir -p "deps/cache"
|
|
pushd deps || return 1
|
|
if ! libsecp256k1_install; then
|
|
echo "libsecp256k1 was not built. Exiting."
|
|
return 1
|
|
fi
|
|
if [[ "${install_os}" != 'debian' ]]; then
|
|
if ! libffi_install; then
|
|
echo "Libffi was not built. Exiting."
|
|
return 1
|
|
fi
|
|
fi
|
|
if ! libsodium_install; then
|
|
echo "Libsodium was not built. Exiting."
|
|
return 1
|
|
fi
|
|
if [[ ${build_local_tor} == "1" ]]; then
|
|
if ! tor_install; then
|
|
echo "Building local Tor was requested, but not built. Exiting."
|
|
return 1
|
|
fi
|
|
fi
|
|
popd || return 1
|
|
if ! joinmarket_install; then
|
|
echo "Joinmarket was not installed. Exiting."
|
|
if [ "$with_jmvenv" == 1 ]; then deactivate; fi
|
|
return 1
|
|
fi
|
|
if [ "$with_jmvenv" == 1 ]; then
|
|
deactivate
|
|
echo "Joinmarket successfully installed
|
|
Before executing scripts or tests, run:
|
|
|
|
\`source jmvenv/bin/activate\`
|
|
|
|
from this directory, to activate the virtual environment."
|
|
fi
|
|
}
|
|
main "${@}"
|