Monero (XMR) Mining Script

This commit is contained in:
JustinOros
2025-11-10 09:55:01 -07:00
parent ca161c523b
commit 3783267593
+294
View File
@@ -0,0 +1,294 @@
#!/usr/bin/env python3
# Description: Monero (XMR) Mining Script - Optimized for CPU mining with support for solo and pool mining
# Usage: python3 miner-monero.py
# Author: Justin Oros
# Source: https://github.com/JustinOros
import json
import subprocess
import sys
import os
import time
import signal
import platform
from pathlib import Path
from typing import Dict, Optional
class MoneroMiner:
def __init__(self, config_file: str = "miner-monero.json"):
self.config_file = config_file
self.config = self.load_config()
self.process: Optional[subprocess.Popen] = None
def load_config(self) -> Dict:
"""Load configuration from JSON file"""
if not os.path.exists(self.config_file):
print(f"Configuration file '{self.config_file}' not found.")
print("Creating example configuration file...")
self.create_example_config()
sys.exit(1)
try:
with open(self.config_file, 'r') as f:
config = json.load(f)
self.validate_config(config)
return config
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in configuration file: {e}")
sys.exit(1)
except Exception as e:
print(f"Error loading configuration: {e}")
sys.exit(1)
def validate_config(self, config: Dict):
"""Validate required configuration fields"""
required_fields = ['wallet_address', 'mining_mode']
for field in required_fields:
if field not in config:
raise ValueError(f"Missing required field: {field}")
if config['mining_mode'] not in ['solo', 'pool']:
raise ValueError("mining_mode must be 'solo' or 'pool'")
if config['mining_mode'] == 'pool' and 'pool_address' not in config:
raise ValueError("pool_address is required for pool mining")
def create_example_config(self):
"""Create an example configuration file with platform-specific defaults"""
is_mac = platform.system() == 'Darwin'
is_arm = platform.machine().lower() in ['arm64', 'aarch64']
example_config = {
"wallet_address": "YOUR_MONERO_WALLET_ADDRESS_HERE",
"mining_mode": "pool",
"miner_executable": "xmrig" if not is_mac else "./xmrig",
"pool_address": "pool.supportxmr.com:443",
"pool_password": "x",
"worker_name": f"m2-miner" if is_arm else "worker1",
"node_address": "127.0.0.1:18081",
"threads": 0,
"cpu_max_usage": 75,
"huge_pages": True if not is_mac else False,
"randomx_mode": "auto",
"tls_enabled": True,
"nicehash": False,
"extra_args": [],
"log_file": "monero-miner.log",
"api_port": 0,
"donate_level": 1
}
with open(self.config_file, 'w') as f:
json.dump(example_config, f, indent=4)
print(f"Created example configuration file: {self.config_file}")
print("\n" + "="*60)
print("IMPORTANT: Edit the configuration file and set your wallet!")
print("="*60)
if is_mac:
print("\nFor Mac users:")
print("1. Download XMRig from: https://github.com/xmrig/xmrig/releases")
print("2. Extract and place 'xmrig' binary in the same folder as this script")
print("3. Run: chmod +x xmrig")
print("\nPopular Monero pools:")
print(" - SupportXMR: pool.supportxmr.com:443")
print(" - MoneroOcean: gulf.moneroocean.stream:10128")
print(" - Nanopool: xmr-us-east1.nanopool.org:14433")
print(" - MineXMR: pool.minexmr.com:443")
def build_command(self) -> list:
"""Build the XMRig mining command based on configuration"""
cmd = [self.config.get('miner_executable', 'xmrig')]
# Add wallet address
wallet = self.config['wallet_address']
if wallet == "YOUR_MONERO_WALLET_ADDRESS_HERE":
print("Error: Please set your Monero wallet address in the config file!")
sys.exit(1)
# Configure based on mining mode
if self.config['mining_mode'] == 'solo':
# Solo mining - connect to local node
node_addr = self.config.get('node_address', '127.0.0.1:18081')
cmd.extend(['--daemon', '--daemon-poll-interval=1000'])
cmd.extend(['--url', node_addr])
cmd.extend(['--user', wallet])
print(f"Solo mining mode - connecting to node at {node_addr}")
else:
# Pool mining
pool_addr = self.config['pool_address']
cmd.extend(['--url', pool_addr])
cmd.extend(['--user', wallet])
# Add worker name if specified
worker = self.config.get('worker_name')
if worker:
cmd.extend(['--rig-id', worker])
# Pool password
pool_pass = self.config.get('pool_password', 'x')
cmd.extend(['--pass', pool_pass])
# TLS
if self.config.get('tls_enabled', True):
cmd.append('--tls')
# NiceHash mode
if self.config.get('nicehash', False):
cmd.append('--nicehash')
print(f"Pool mining mode - connecting to pool at {pool_addr}")
# CPU Thread configuration
threads = self.config.get('threads', 0)
if threads > 0:
cmd.extend(['--threads', str(threads)])
# CPU max usage
cpu_max = self.config.get('cpu_max_usage')
if cpu_max:
cmd.extend(['--cpu-max-threads-hint', str(cpu_max)])
# Huge pages (not supported on macOS)
if self.config.get('huge_pages', False) and platform.system() != 'Darwin':
cmd.append('--hugepages')
# RandomX mode
rx_mode = self.config.get('randomx_mode', 'auto')
if rx_mode in ['light', 'fast']:
cmd.extend(['--randomx-mode', rx_mode])
# API port for monitoring
api_port = self.config.get('api_port', 0)
if api_port > 0:
cmd.extend(['--http-port', str(api_port)])
cmd.append('--http-enabled')
# Donate level
donate = self.config.get('donate_level', 1)
cmd.extend(['--donate-level', str(donate)])
# Add any extra arguments
extra_args = self.config.get('extra_args', [])
if extra_args:
cmd.extend(extra_args)
return cmd
def start_mining(self):
"""Start the mining process"""
cmd = self.build_command()
print("\n" + "="*60)
print("Monero Miner Starting")
print("="*60)
print(f"Wallet: {self.config['wallet_address'][:20]}...{self.config['wallet_address'][-10:]}")
print(f"Mode: {self.config['mining_mode'].upper()}")
print(f"Platform: {platform.system()} ({platform.machine()})")
print(f"Command: {' '.join(cmd)}")
print("="*60 + "\n")
# Setup logging if specified
log_file = self.config.get('log_file')
log_handle = None
try:
if log_file:
log_handle = open(log_file, 'a')
log_handle.write(f"\n{'='*60}\n")
log_handle.write(f"Mining started at {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
log_handle.write(f"Command: {' '.join(cmd)}\n")
log_handle.write(f"{'='*60}\n\n")
log_handle.flush()
# Start the miner process
self.process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE if log_handle else None,
stderr=subprocess.STDOUT if log_handle else None,
universal_newlines=True,
bufsize=1
)
# Monitor output if logging
if log_handle and self.process.stdout:
try:
for line in self.process.stdout:
print(line, end='')
log_handle.write(line)
log_handle.flush()
except KeyboardInterrupt:
print("\n\nReceived interrupt signal. Stopping miner...")
self.stop_mining()
else:
# Wait for process to complete
self.process.wait()
except FileNotFoundError:
print(f"\nError: Miner executable '{cmd[0]}' not found.")
print("\nFor macOS/Apple Silicon:")
print("1. Download XMRig from: https://github.com/xmrig/xmrig/releases")
print("2. Look for 'xmrig-X.X.X-macos-arm64.tar.gz' for M2 Mac")
print("3. Extract and place 'xmrig' in the same folder as this script")
print("4. Run: chmod +x xmrig")
print("\nFor Linux:")
print(" sudo apt install xmrig # or download from GitHub")
sys.exit(1)
except Exception as e:
print(f"Error starting miner: {e}")
sys.exit(1)
finally:
if log_handle:
log_handle.close()
def stop_mining(self):
"""Stop the mining process"""
if self.process:
print("Stopping miner...")
self.process.terminate()
try:
self.process.wait(timeout=10)
except subprocess.TimeoutExpired:
print("Force killing miner process...")
self.process.kill()
print("Miner stopped.")
def show_stats(self):
"""Display mining statistics if API is enabled"""
api_port = self.config.get('api_port', 0)
if api_port > 0:
print(f"\nMining stats available at: http://127.0.0.1:{api_port}")
print("You can view real-time statistics in your browser")
def signal_handler(signum, frame):
"""Handle interrupt signals"""
print("\n\nShutdown signal received...")
sys.exit(0)
def main():
# Register signal handlers
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# Parse command line arguments
config_file = "miner-monero.json"
if len(sys.argv) > 1:
config_file = sys.argv[1]
# Create and start miner
miner = MoneroMiner(config_file)
try:
miner.show_stats()
miner.start_mining()
except KeyboardInterrupt:
print("\n\nInterrupted by user")
miner.stop_mining()
except Exception as e:
print(f"\nError: {e}")
miner.stop_mining()
sys.exit(1)
if __name__ == "__main__":
main()