aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorMonzero Build System <builds@monzero.org>2026-08-15 21:14:26 +0100
committerMonzero Build System <builds@monzero.org>2026-08-15 21:14:26 +0100
commit28f1919249465d3230f45ed21686c5a836d56df0 (patch)
tree948fe5e7052c45c366c24a82b485ae9d1d38e39b /utils
parent4f92268d7c16741cfb41e5bbe2aa46cc260a9ea5 (diff)
downloadmonzero-core-28f1919249465d3230f45ed21686c5a836d56df0.tar.gz
monzero-core-28f1919249465d3230f45ed21686c5a836d56df0.tar.xz
monzero-core-28f1919249465d3230f45ed21686c5a836d56df0.zip
Establish Monzero Phase 0 and inactive assets baselinemonzero-phase0-assets-prototype-20260815
Diffstat (limited to 'utils')
-rw-r--r--utils/configure-miner-stats.php30
-rwxr-xr-xutils/monzero-miner-reporter.py93
-rwxr-xr-xutils/release/package-linux.sh84
-rwxr-xr-xutils/release/verify-package.sh53
4 files changed, 260 insertions, 0 deletions
diff --git a/utils/configure-miner-stats.php b/utils/configure-miner-stats.php
new file mode 100644
index 000000000..7b2f72d34
--- /dev/null
+++ b/utils/configure-miner-stats.php
@@ -0,0 +1,30 @@
+<?php
+declare(strict_types=1);
+
+if (PHP_SAPI !== 'cli' || $argc !== 3) {
+ fwrite(STDERR, "Usage: php configure-miner-stats.php CONFIG_PATH DATA_PATH\n");
+ exit(1);
+}
+
+$configPath = $argv[1];
+$dataPath = $argv[2];
+if (file_exists($configPath)) {
+ fwrite(STDERR, "Refusing to overwrite existing configuration: {$configPath}\n");
+ exit(1);
+}
+
+$serverSecret = bin2hex(random_bytes(32));
+$ingestToken = bin2hex(random_bytes(32));
+$config = "<?php\nreturn " . var_export([
+ 'MONZERO_STATS_SECRET' => $serverSecret,
+ 'MONZERO_STATS_INGEST_TOKEN' => $ingestToken,
+ 'MONZERO_STATS_FILE' => $dataPath,
+], true) . ";\n";
+
+if (file_put_contents($configPath, $config, LOCK_EX) === false) {
+ fwrite(STDERR, "Unable to create configuration.\n");
+ exit(1);
+}
+chmod($configPath, 0600);
+
+fwrite(STDOUT, "Configuration created. Team ingest token (save privately):\n{$ingestToken}\n");
diff --git a/utils/monzero-miner-reporter.py b/utils/monzero-miner-reporter.py
new file mode 100755
index 000000000..cbbc3029a
--- /dev/null
+++ b/utils/monzero-miner-reporter.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+"""Opt-in anonymous mining telemetry reporter for monzero.org."""
+
+from __future__ import annotations
+
+import argparse
+import json
+import os
+import re
+import stat
+import time
+import urllib.error
+import urllib.request
+import uuid
+from pathlib import Path
+
+
+FOUND_BLOCK = re.compile(r"Found block [0-9a-f]+ at height ", re.IGNORECASE)
+
+
+def json_request(url: str, body: dict | None = None, token: str = "") -> dict:
+ data = None if body is None else json.dumps(body).encode("utf-8")
+ headers = {"Accept": "application/json"}
+ if data is not None:
+ headers["Content-Type"] = "application/json"
+ if token:
+ headers["Authorization"] = f"Bearer {token}"
+ request = urllib.request.Request(url, data=data, headers=headers)
+ with urllib.request.urlopen(request, timeout=8) as response:
+ return json.load(response)
+
+
+def installation_id(path: Path) -> str:
+ if path.exists():
+ return path.read_text(encoding="ascii").strip()
+ path.parent.mkdir(parents=True, exist_ok=True)
+ value = str(uuid.uuid4())
+ path.write_text(value + "\n", encoding="ascii")
+ path.chmod(stat.S_IRUSR | stat.S_IWUSR)
+ return value
+
+
+def blocks_found(log_path: Path) -> int:
+ try:
+ with log_path.open("r", encoding="utf-8", errors="ignore") as log:
+ return sum(1 for line in log if FOUND_BLOCK.search(line))
+ except FileNotFoundError:
+ return 0
+
+
+def report(args: argparse.Namespace) -> None:
+ status = json_request(args.rpc.rstrip("/") + "/mining_status", {})
+ payload = {
+ "installation_id": installation_id(args.id_file),
+ "hashrate": int(status.get("speed", 0)) if status.get("active") else 0,
+ "blocks_found": blocks_found(args.log),
+ }
+ result = json_request(args.endpoint, payload, args.token)
+ if result.get("status") != "OK":
+ raise RuntimeError(f"telemetry rejected: {result}")
+ print(
+ f"reported {payload['hashrate']} H/s and "
+ f"{payload['blocks_found']} block(s); no wallet or hardware ID sent"
+ )
+
+
+def main() -> None:
+ default_data = Path.home() / ".monzero"
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--endpoint", default="https://monzero.org/api/miner-stats/")
+ parser.add_argument("--rpc", default="http://127.0.0.1:6175")
+ parser.add_argument("--token", default=os.environ.get("MONZERO_STATS_TOKEN", ""))
+ parser.add_argument("--id-file", type=Path, default=default_data / "telemetry-id")
+ parser.add_argument("--log", type=Path, default=default_data / "monzero.log")
+ parser.add_argument("--interval", type=int, default=0, help="repeat every N seconds")
+ args = parser.parse_args()
+ if not args.token:
+ parser.error("set MONZERO_STATS_TOKEN or pass --token")
+ if args.interval and args.interval < 30:
+ parser.error("--interval must be at least 30 seconds")
+
+ while True:
+ try:
+ report(args)
+ except (OSError, ValueError, urllib.error.URLError, RuntimeError) as error:
+ print(f"report failed: {error}")
+ if not args.interval:
+ break
+ time.sleep(args.interval)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/utils/release/package-linux.sh b/utils/release/package-linux.sh
new file mode 100755
index 000000000..bcc659e57
--- /dev/null
+++ b/utils/release/package-linux.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+usage() {
+ echo "Usage: $0 <build-bin-dir> <output-dir> <version>" >&2
+ echo "Set ALLOW_DIRTY=1 only for an explicitly marked development artifact." >&2
+ exit 2
+}
+
+[[ $# -eq 3 ]] || usage
+
+source_root=$(git rev-parse --show-toplevel)
+build_bin=$(realpath "$1")
+output_dir=$(realpath -m "$2")
+version=$3
+
+[[ $version =~ ^[0-9A-Za-z][0-9A-Za-z._-]*$ ]] || { echo "Invalid version label" >&2; exit 2; }
+[[ -x "$build_bin/monzerod" ]] || { echo "Missing executable: $build_bin/monzerod" >&2; exit 2; }
+[[ -x "$build_bin/monzero-wallet-cli" ]] || { echo "Missing executable: $build_bin/monzero-wallet-cli" >&2; exit 2; }
+
+source_commit=$(git -C "$source_root" rev-parse HEAD)
+source_epoch=${SOURCE_DATE_EPOCH:-$(git -C "$source_root" show -s --format=%ct "$source_commit")}
+[[ $source_epoch =~ ^[0-9]+$ ]] || { echo "SOURCE_DATE_EPOCH must be an integer" >&2; exit 2; }
+
+dirty=false
+if [[ -n $(git -C "$source_root" status --porcelain --untracked-files=normal) ]]; then
+ dirty=true
+ [[ ${ALLOW_DIRTY:-0} == 1 ]] || {
+ echo "Refusing to package a dirty source tree. Commit the reviewed baseline first." >&2
+ exit 1
+ }
+ version="${version}-development-dirty"
+fi
+
+package_name="monzero-${version}-linux-x86_64"
+mkdir -p "$output_dir"
+work_dir=$(mktemp -d "${TMPDIR:-/tmp}/monzero-package.XXXXXX")
+trap 'rm -rf -- "$work_dir"' EXIT
+package_dir="$work_dir/$package_name"
+mkdir -p "$package_dir"
+
+install -m 0755 "$build_bin/monzerod" "$package_dir/monzerod"
+install -m 0755 "$build_bin/monzero-wallet-cli" "$package_dir/monzero-wallet-cli"
+for optional in monzero-wallet-rpc monzero-gen-ssl-cert; do
+ [[ -x "$build_bin/$optional" ]] && install -m 0755 "$build_bin/$optional" "$package_dir/$optional"
+done
+
+install -m 0755 "$source_root/start-monzerod.sh" "$package_dir/start-monzerod.sh"
+install -m 0755 "$source_root/start-monzero-wallet-cli.sh" "$package_dir/start-monzero-wallet-cli.sh"
+install -m 0755 "$source_root/start-monzero-miner.sh" "$package_dir/start-monzero-miner.sh"
+install -m 0755 "$source_root/stop-monzero-miner.sh" "$package_dir/stop-monzero-miner.sh"
+install -m 0644 "$source_root/LICENSE" "$package_dir/LICENSE"
+install -m 0644 "$source_root/MONZERO_CHAIN_SPEC.md" "$package_dir/MONZERO_CHAIN_SPEC.md"
+install -m 0644 "$source_root/docs/RELEASE_CHECKLIST.md" "$package_dir/RELEASE_CHECKLIST.md"
+
+{
+ echo "package=$package_name"
+ echo "source_commit=$source_commit"
+ echo "source_date_epoch=$source_epoch"
+ echo "source_tree_dirty=$dirty"
+ echo "binary_build_reproducibility=unverified"
+ echo "assets_consensus_enabled=false"
+ echo "native_ticker=XMZ"
+} > "$package_dir/BUILD-MANIFEST.txt"
+
+(
+ cd "$package_dir"
+ find . -maxdepth 1 -type f ! -name SHA256SUMS -printf '%P\n' \
+ | LC_ALL=C sort \
+ | xargs -r sha256sum > SHA256SUMS
+ sha256sum -c SHA256SUMS
+)
+
+archive="$output_dir/$package_name.tar.gz"
+tar --sort=name \
+ --mtime="@$source_epoch" \
+ --owner=0 --group=0 --numeric-owner \
+ --pax-option=delete=atime,delete=ctime \
+ -C "$work_dir" -cf - "$package_name" \
+ | gzip -n -9 > "$archive"
+sha256sum "$archive" > "$archive.sha256"
+
+echo "$archive"
+echo "$archive.sha256"
diff --git a/utils/release/verify-package.sh b/utils/release/verify-package.sh
new file mode 100755
index 000000000..22ccab4c8
--- /dev/null
+++ b/utils/release/verify-package.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+[[ $# -eq 1 ]] || { echo "Usage: $0 <monzero-linux.tar.gz>" >&2; exit 2; }
+archive=$(realpath "$1")
+[[ -f "$archive" ]] || { echo "Archive not found: $archive" >&2; exit 2; }
+
+work_dir=$(mktemp -d "${TMPDIR:-/tmp}/monzero-verify.XXXXXX")
+trap 'rm -rf -- "$work_dir"' EXIT
+tar -xzf "$archive" -C "$work_dir"
+
+mapfile -t roots < <(find "$work_dir" -mindepth 1 -maxdepth 1 -type d)
+[[ ${#roots[@]} -eq 1 ]] || { echo "Archive must contain exactly one root directory" >&2; exit 1; }
+root=${roots[0]}
+
+[[ -x "$root/monzerod" && -x "$root/monzero-wallet-cli" ]] || {
+ echo "Required Monzero executables are missing" >&2
+ exit 1
+}
+[[ -f "$root/BUILD-MANIFEST.txt" && -f "$root/SHA256SUMS" ]] || {
+ echo "Build or checksum manifest is missing" >&2
+ exit 1
+}
+
+if find "$root" -type l -print -quit | grep -q .; then
+ echo "Package contains symbolic links" >&2
+ exit 1
+fi
+
+(
+ cd "$root"
+ sha256sum -c SHA256SUMS
+)
+
+binary_report=$(file "$root/monzerod" "$root/monzero-wallet-cli")
+echo "$binary_report"
+if [[ ${RELEASE_STRICT:-0} == 1 ]]; then
+ if grep -Eq 'dynamically linked|not stripped|with debug_info' <<< "$binary_report"; then
+ echo "Strict release verification rejects dynamic, unstripped, or debug binaries" >&2
+ exit 1
+ fi
+ grep -qx 'source_tree_dirty=false' "$root/BUILD-MANIFEST.txt" || {
+ echo "Strict release verification rejects a dirty source manifest" >&2
+ exit 1
+ }
+ grep -qx 'binary_build_reproducibility=verified' "$root/BUILD-MANIFEST.txt" || {
+ echo "Strict release verification requires independently verified binary reproducibility" >&2
+ exit 1
+ }
+fi
+"$root/monzerod" --version
+"$root/monzero-wallet-cli" --version
+echo "Package verification passed: $archive"