#!/bin/sh # FearMiner, installed in one line (Linux, macOS): # # curl -fsSL https://get.fearminer.com | sh # install, keep it running # curl -fsSL https://get.fearminer.com | sh -s -- fm1_... # ... and join your cockpit's fleet # curl -fsSL https://get.fearminer.com | sh -s -- --wallet ADDRESS --pool stratum+ssl://POOL:PORT --worker rig1 # # ... and mine to it on your pool # # It downloads the latest release, checks it against the release key, and # installs FearMiner as a service of your account (no sudo) that starts with # the machine. What it mines comes from your cockpit (a mining sheet) or # from --wallet with --pool: the pool is yours to choose, a wallet never # goes without one. # # Options: fm1_... --wallet ADDRESS --pool URL [--worker NAME] --system # (the machine's service, with sudo) --no-service (the binary only, in # ~/.local/bin) --help # # The check: the SHA256SUMS of the release is verified against the release # key below with minisign when it is installed, with openssl otherwise (an # Ed25519 check of the same signature); without either, the SHA256SUMS of # download.fearminer.com must be the same as GitHub's. Then the archive # against SHA256SUMS. Nothing is run before all of it holds. set -eu RELEASE_KEY="RWQR3owV+nRhfgNJeUBqcRK868S52NFG2BOrIzKpkQ5ey6lCEfM9+3Eg" BASE="${FEARMINER_DOWNLOAD:-https://download.fearminer.com/latest}" GITHUB="https://github.com/fearminer/fearminer/releases/latest/download" code="" wallet="" pool="" worker="" system="" service=1 say() { printf '%s\n' "fearminer: $*"; } die() { printf '%s\n' "fearminer: $*" >&2; exit 1; } while [ $# -gt 0 ]; do case "$1" in fm1_*) code="$1" ;; --wallet) [ $# -ge 2 ] || die "--wallet needs an address"; wallet="$2"; shift ;; --wallet=*) wallet="${1#--wallet=}" ;; --pool) [ $# -ge 2 ] || die "--pool needs a URL (stratum+ssl://POOL:PORT)"; pool="$2"; shift ;; --pool=*) pool="${1#--pool=}" ;; --worker) [ $# -ge 2 ] || die "--worker needs a name"; worker="$2"; shift ;; --worker=*) worker="${1#--worker=}" ;; --system) system=1 ;; --no-service) service="" ;; -h|--help) sed -n '2,23p' "$0" 2>/dev/null || true; exit 0 ;; *) die "unknown argument '$1' (--help)" ;; esac shift done # A wallet always comes with its pool, and the pool with a wallet: the # pool is the operator's choice, never made for them. if [ -n "$wallet" ] && [ -z "$pool" ]; then die "--wallet needs --pool: --wallet ADDRESS --pool stratum+ssl://POOL:PORT [--worker rig1] (the pool is yours to choose)" fi if [ -z "$wallet" ] && { [ -n "$pool" ] || [ -n "$worker" ]; }; then die "--pool and --worker go with --wallet: --wallet ADDRESS --pool stratum+ssl://POOL:PORT [--worker rig1]" fi # Where the machine is. if [ -d /hive ] && [ -f /hive-config/rig.conf ] 2>/dev/null; then die "this is HiveOS: the flight sheet runs FearMiner. Put --enroll fm1_... in its extra arguments instead." fi if [ -d /opt/mmp ] || [ -d /mmp-config ]; then die "this is mmpOS: a miner profile runs FearMiner, as a custom miner (the fearminer_mmpos package of the release). Put --enroll fm1_... in its Arguments instead." fi os=$(uname -s) arch=$(uname -m) case "$os/$arch" in Linux/x86_64 | Linux/amd64) asset="fearminer-linux-x86_64.tar.gz" ;; Darwin/arm64) asset="fearminer-macos-arm64.tar.gz" ;; *) die "no release for $os $arch yet (Linux x86_64, macOS Apple silicon, Windows: see https://fearminer.com)" ;; esac if command -v curl >/dev/null 2>&1; then fetch() { curl -fsSL --retry 3 -o "$2" "$1"; } elif command -v wget >/dev/null 2>&1; then fetch() { wget -q -O "$2" "$1"; } else die "curl or wget is needed" fi if command -v sha256sum >/dev/null 2>&1; then sha256() { sha256sum "$1" | cut -d' ' -f1; } elif command -v shasum >/dev/null 2>&1; then sha256() { shasum -a 256 "$1" | cut -d' ' -f1; } else die "sha256sum or shasum is needed" fi tmp=$(mktemp -d 2>/dev/null || mktemp -d -t fearminer) trap 'rm -rf "$tmp"' EXIT INT TERM cd "$tmp" say "downloading the latest release for $os $arch" fetch "$BASE/SHA256SUMS" SHA256SUMS || die "cannot reach $BASE" fetch "$BASE/SHA256SUMS.minisig" SHA256SUMS.minisig || die "the release has no signature at $BASE" # 1. SHA256SUMS against the release key. verified="" if command -v minisign >/dev/null 2>&1; then minisign -q -Vm SHA256SUMS -P "$RELEASE_KEY" >/dev/null 2>&1 || die "the release signature does not hold (minisign): nothing installed" verified="minisign" elif command -v openssl >/dev/null 2>&1 && openssl version 2>/dev/null | grep -q '^OpenSSL [3-9]'; then printf '%s' "$RELEASE_KEY" | base64 -d > pub.bin 2>/dev/null || printf '%s' "$RELEASE_KEY" | base64 -D > pub.bin sed -n 2p SHA256SUMS.minisig | { base64 -d 2>/dev/null || base64 -D; } > sig.bin [ "$(head -c 2 sig.bin)" = "ED" ] || die "the release signature is not in the expected form: nothing installed" [ "$(tail -c +3 pub.bin | head -c 8 | od -An -tx1)" = "$(tail -c +3 sig.bin | head -c 8 | od -An -tx1)" ] || die "the release is signed by another key: nothing installed" { printf '\060\052\060\005\006\003\053\145\160\003\041\000'; tail -c 32 pub.bin; } > pub.der tail -c 64 sig.bin > s.bin openssl dgst -blake2b512 -binary SHA256SUMS > h.bin openssl pkeyutl -verify -pubin -inkey pub.der -keyform DER -rawin -in h.bin -sigfile s.bin >/dev/null 2>&1 || die "the release signature does not hold (openssl): nothing installed" verified="openssl" else # No tool to check a signature: two independent hosts must agree. fetch "$GITHUB/SHA256SUMS" SHA256SUMS.github || die "cannot reach GitHub to cross-check the release" a=$(grep " $asset\$" SHA256SUMS || true) b=$(grep " $asset\$" SHA256SUMS.github || true) [ -n "$a" ] && [ "$a" = "$b" ] || die "download.fearminer.com and GitHub disagree on $asset: nothing installed" verified="GitHub cross-check (install minisign or openssl 3 for the signature check)" fi # 2. The archive against SHA256SUMS. expected=$(grep " $asset\$" SHA256SUMS | cut -d' ' -f1) [ -n "$expected" ] || die "SHA256SUMS does not list $asset" fetch "$BASE/$asset" "$asset" || die "cannot download $asset" [ "$(sha256 "$asset")" = "$expected" ] || die "$asset does not match its SHA-256: nothing installed" say "verified ($verified)" tar xzf "$asset" dir=$(find . -maxdepth 1 -type d -name 'fearminer-*' | head -n 1) [ -n "$dir" ] && [ -x "$dir/fearminer" ] || die "the archive has no fearminer binary" if [ "$os" = Darwin ]; then # Downloaded files carry Gatekeeper's quarantine flag; the binary is not # notarised, so macOS would refuse it. Said, since it lifts a check. xattr -dr com.apple.quarantine "$dir" 2>/dev/null || true say "macOS: the quarantine flag is lifted from the checked download (the binary is not notarised yet)" fi version=$("$dir/fearminer" --version 2>/dev/null | head -n 1 || true) if [ -z "$service" ]; then mkdir -p "$HOME/.local/bin" cp "$dir/fearminer" "$HOME/.local/bin/fearminer" [ -x "$dir/fearminer-helper" ] && cp "$dir/fearminer-helper" "$HOME/.local/bin/fearminer-helper" say "$version installed in ~/.local/bin" [ -n "$code" ] && "$HOME/.local/bin/fearminer" enroll "$code" exit 0 fi # 3. The service: it copies the binary to its fixed place and starts it. set -- service install [ -n "$system" ] && set -- "$@" --system [ -n "$code" ] && set -- "$@" --enroll "$code" [ -n "$wallet" ] && set -- "$@" -o "$pool" -u "$wallet" [ -n "$worker" ] && set -- "$@" -w "$worker" if [ -n "$system" ] && [ "$(id -u)" != 0 ]; then sudo "$dir/fearminer" "$@" else "$dir/fearminer" "$@" fi say "$version is installed and running." if [ -n "$code" ]; then say "it shows up in your cockpit within seconds; choose what it mines there." elif [ -z "$wallet" ]; then say "it watches the cards and mines nothing yet: add it to a cockpit (https://app.fearminer.com, Add a rig)" say "or mine at once: curl -fsSL https://get.fearminer.com | sh -s -- --wallet YOUR_ADDRESS --pool stratum+ssl://YOUR_POOL:PORT --worker rig1" fi