Преглед на файлове

minerd: rip sweet prince

skoupidi преди 7 месеца
родител
ревизия
18cbb5ae2d

+ 0 - 46
bin/minerd/Cargo.toml

@@ -1,46 +0,0 @@
-[package]
-name = "minerd"
-version = "0.5.0"
-homepage = "https://dark.fi"
-description = "Darkfi mining daemon"
-authors = ["Dyne.org foundation <foundation@dyne.org>"]
-repository = "https://codeberg.org/darkrenaissance/darkfi"
-license = "AGPL-3.0-only"
-edition = "2021"
-
-[dependencies]
-libc = "0.2.178"
-parking_lot = "0.12.5"
-once_cell = "1.21.3"
-
-# Darkfi
-darkfi = {path = "../../", features = ["async-daemonize", "validator", "rpc", "bs58"]}
-darkfi-sdk = {path = "../../src/sdk"}
-darkfi-serial = {version = "0.5.0", features = ["async"]}
-
-# Misc
-bs58 = "0.5.1"
-randomx = {git = "https://codeberg.org/darkrenaissance/RandomX"}
-tracing = "0.1.44"
-num-bigint = "0.4.6"
-
-# JSON-RPC
-tinyjson = "2.5.1"
-url = "2.5.7"
-
-# Daemon
-easy-parallel = "3.3.1"
-signal-hook-async-std = "0.3.0"
-signal-hook = "0.3.18"
-tracing-subscriber = { version = "0.3.22", default-features = false, features = ["fmt"] }
-tracing-appender = "0.2.4"
-smol = "2.0.2"
-
-# Argument parsing
-serde = {version = "1.0.228", features = ["derive"]}
-structopt = "0.3.26"
-structopt-toml = "0.5.1"
-toml = "0.9.8"
-
-[lints]
-workspace = true

+ 0 - 41
bin/minerd/Makefile

@@ -1,41 +0,0 @@
-.POSIX:
-
-# Install prefix
-PREFIX = $(HOME)/.cargo
-
-# Cargo binary
-CARGO = cargo
-
-# Compile target
-RUST_TARGET = $(shell rustc -Vv | grep '^host: ' | cut -d' ' -f2)
-# Uncomment when doing musl static builds
-#RUSTFLAGS = -C target-feature=+crt-static -C link-self-contained=yes
-
-SRC = \
-	Cargo.toml \
-	../../Cargo.toml \
-	$(shell find src -type f -name '*.rs') \
-	$(shell find ../../src -type f -name '*.rs')
-
-BIN = $(shell grep '^name = ' Cargo.toml | cut -d' ' -f3 | tr -d '"')
-
-all: $(BIN)
-
-$(BIN): $(SRC)
-	RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build --target=$(RUST_TARGET) --release --package $@
-	cp -f ../../target/$(RUST_TARGET)/release/$@ $@
-	cp -f ../../target/$(RUST_TARGET)/release/$@ ../../$@
-
-clean:
-	RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clean --target=$(RUST_TARGET) --release --package $(BIN)
-	rm -f $(BIN) ../../$(BIN)
-
-install: all
-	mkdir -p $(DESTDIR)$(PREFIX)/bin
-	cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin
-	chmod 755 $(DESTDIR)$(PREFIX)/bin/$(BIN)
-
-uninstall:
-	rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN)
-
-.PHONY: all clean install uninstall

+ 0 - 79
bin/minerd/minerd.toml

@@ -1,79 +0,0 @@
-## minerd configuration file
-##
-## Please make sure you go through all the settings so you can configure
-## your daemon properly.
-##
-## The default values are left commented. They can be overridden either by
-## uncommenting, or by using the command-line.
-
-# Number of nonces to execute in system hashrate benchmark
-#bench = 1000
-
-# Flag indicating whether to run miner in light mode
-light_mode = false
-
-# Flag indicating whether to run miner with Large Pages
-large_pages = false
-
-# Flag indicating whether to run miner with secure access to JIT memory (if supported)
-secure = false
-
-# PoW miner number of threads to use
-#threads = 4
-
-# Assign full L3 cache to CPU
-cache_qos = false
-
-# Polling rate to ask darkfid for mining jobs
-#polling_rate = 2
-
-# Stop mining at given height (0 mines forever)
-#stop_at_height = 0
-
-# Blockchain network to use
-network = "testnet"
-
-# Localnet blockchain network configuration
-[network_config."localnet"]
-# Wallet mining address to receive mining rewards
-recipient = "YOUR_WALLET_ADDRESS_HERE"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
-
-# darkfid JSON-RPC endpoint
-endpoint = "tcp://127.0.0.1:8240"
-
-# Testnet blockchain network configuration
-[network_config."testnet"]
-# Wallet mining address to receive mining rewards
-recipient = "YOUR_WALLET_ADDRESS_HERE"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
-
-# darkfid JSON-RPC endpoint
-endpoint = "tcp://127.0.0.1:8340"
-
-# Mainnet blockchain network configuration
-[network_config."mainnet"]
-# Wallet mining address to receive mining rewards
-recipient = "YOUR_WALLET_ADDRESS_HERE"
-
-# Optional contract spend hook to use in the mining reward
-#spend_hook = "YOUR_SPEND_HOOK_HERE"
-
-# Optional contract user data to use in the mining reward.
-# This is not arbitrary data.
-#user_data = "YOUR_USER_DATA_HERE"
-
-# darkfid JSON-RPC endpoint
-endpoint = "tcp://127.0.0.1:8440"

+ 0 - 105
bin/minerd/src/benchmark.rs

@@ -1,105 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{
-    str::FromStr,
-    sync::{
-        atomic::{AtomicU64, Ordering},
-        Arc,
-    },
-    thread,
-    time::Instant,
-};
-
-use tracing::{error, info};
-
-use darkfi::{
-    blockchain::{Header, HeaderHash},
-    util::time::Timestamp,
-    validator::pow::{generate_mining_vms, get_mining_flags},
-    Result,
-};
-
-/// Performs provided number of nonces simulating mining for provided
-/// threads count to determine system hashrate.
-pub fn benchmark(
-    fast_mode: bool,
-    large_pages: bool,
-    secure: bool,
-    threads: usize,
-    nonces: u64,
-) -> Result<()> {
-    // Check provided params are valid
-    if threads == 0 {
-        error!(target: "minerd::benchmark", "No threads were configured!");
-        return Ok(())
-    }
-
-    if nonces == 0 {
-        error!(target: "minerd::benchmark", "No number of nonces was configured!");
-        return Ok(())
-    }
-    info!(target: "minerd::benchmark", "Starting DarkFi hashrate benchmark for {threads} threads and {nonces} nonces");
-
-    // Setup VMs using a dummy key for reproducible results
-    let key =
-        HeaderHash::from_str("c09967802bab1a95a4c434f18beb5a79e68ec7c75b252eb47e56516f32db8ce1")?;
-    info!(target: "minerd::benchmark", "Initializing {threads} VMs for key: {key}");
-    let (_, recvr) = smol::channel::bounded(1);
-    let flags = get_mining_flags(fast_mode, large_pages, secure);
-    let vms = generate_mining_vms(flags, &key, threads, &recvr)?;
-
-    // Use a dummy header to mine for reproducible results
-    let header = Header::new(key, 1, Timestamp::from_u64(1765378623), 0);
-
-    // Start mining
-    info!(target: "minerd::benchmark", "Starting mining threads...");
-    let mut handles = Vec::with_capacity(vms.len());
-    let atomic_nonce = Arc::new(AtomicU64::new(0));
-    let threads = vms.len() as u64;
-    let mining_start = Instant::now();
-    for t in 0..threads {
-        let vm = vms[t as usize].clone();
-        let mut thread_header = header.clone();
-        let atomic_nonce = atomic_nonce.clone();
-
-        handles.push(thread::spawn(move || {
-            thread_header.nonce = atomic_nonce.fetch_add(1, Ordering::SeqCst);
-            vm.calculate_hash_first(thread_header.hash().inner()).unwrap();
-            while thread_header.nonce < nonces {
-                thread_header.nonce = atomic_nonce.fetch_add(1, Ordering::SeqCst);
-                let _ = vm.calculate_hash_next(thread_header.hash().inner()).unwrap();
-            }
-        }));
-    }
-
-    // Wait for threads to finish mining
-    for handle in handles {
-        let _ = handle.join();
-    }
-
-    // Print total results
-    let elapsed = mining_start.elapsed();
-    let hashrate = if elapsed.as_secs_f64() == 0.0 {
-        nonces as f64 * 1000.0 / elapsed.as_millis() as f64
-    } else {
-        nonces as f64 / elapsed.as_secs_f64()
-    };
-    info!(target: "minerd::benchmark", "Threads completed {nonces} nonces in {elapsed:?} with hashrate: {hashrate:.2} h/s");
-    Ok(())
-}

+ 0 - 169
bin/minerd/src/cpu.rs

@@ -1,169 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::io;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct CpuThread {
-    affinity: i32,
-    intensity: u32,
-}
-
-impl CpuThread {
-    pub fn new(affinity: i32, intensity: Option<u32>) -> Self {
-        Self { affinity, intensity: intensity.unwrap_or(0) }
-    }
-
-    #[inline]
-    pub fn is_valid(&self) -> bool {
-        self.intensity <= 8
-    }
-
-    #[inline]
-    pub fn affinity(&self) -> i32 {
-        self.affinity
-    }
-
-    #[inline]
-    pub fn intensity(&self) -> u32 {
-        if self.intensity == 0 {
-            1
-        } else {
-            self.intensity
-        }
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct CpuThreads {
-    affinity: i32,
-    data: Vec<CpuThread>,
-}
-
-impl CpuThreads {
-    pub fn new(count: usize, intensity: u32) -> Self {
-        let mut self_ = Self { affinity: -1, data: Vec::with_capacity(count) };
-
-        for _ in 0..count {
-            self_.add(CpuThread::new(-1, Some(intensity)));
-        }
-
-        self_
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.data.is_empty()
-    }
-
-    pub fn add(&mut self, thread: CpuThread) {
-        self.data.push(thread)
-    }
-
-    pub fn threads(&self) -> &[CpuThread] {
-        &self.data
-    }
-}
-
-#[inline]
-pub fn get_affinity(index: u64, affinity: i32) -> i32 {
-    if affinity < 0 {
-        return -1
-    }
-
-    let affinity = affinity as u64;
-    let mut idx = 0u64;
-
-    for i in 0..64 {
-        if (affinity & (1u64 << i)) == 0 {
-            continue
-        }
-
-        if idx == index {
-            return i
-        }
-
-        idx += 1;
-    }
-
-    -1
-}
-
-/// Binds the current thread to the specified core(s)
-pub fn set_thread_affinity<B: AsRef<[usize]>>(core_ids: B) -> io::Result<()> {
-    os::set_thread_affinity(core_ids.as_ref())
-}
-
-/// Returns a list of cores that the current thread is bound to
-pub fn get_thread_affinity() -> io::Result<Vec<usize>> {
-    os::get_thread_affinity()
-}
-
-// https://github.com/elast0ny/affinity/
-// Licensed under MIT
-#[cfg(target_os = "linux")]
-mod os {
-    use libc::{
-        cpu_set_t, pid_t, sched_getaffinity, sched_setaffinity, CPU_ISSET, CPU_SET, CPU_SETSIZE,
-    };
-    use std::{
-        io,
-        mem::{size_of, zeroed},
-    };
-
-    pub(super) fn set_thread_affinity(core_ids: &[usize]) -> io::Result<()> {
-        let mut set: cpu_set_t = unsafe { zeroed() };
-        unsafe {
-            for core_id in core_ids {
-                CPU_SET(*core_id, &mut set);
-            }
-        }
-
-        _sched_setaffinity(0, size_of::<cpu_set_t>(), &set)
-    }
-
-    pub(super) fn get_thread_affinity() -> io::Result<Vec<usize>> {
-        let mut affinity = vec![];
-        let mut set: cpu_set_t = unsafe { zeroed() };
-
-        _sched_getaffinity(0, size_of::<cpu_set_t>(), &mut set)?;
-
-        for i in 0..CPU_SETSIZE as usize {
-            if unsafe { CPU_ISSET(i, &set) } {
-                affinity.push(i);
-            }
-        }
-
-        Ok(affinity)
-    }
-
-    fn _sched_setaffinity(pid: pid_t, cpusetsize: usize, mask: &cpu_set_t) -> io::Result<()> {
-        let res = unsafe { sched_setaffinity(pid, cpusetsize, mask) };
-        if res != 0 {
-            return Err(io::Error::last_os_error())
-        }
-        Ok(())
-    }
-
-    fn _sched_getaffinity(pid: pid_t, cpusetsize: usize, mask: &mut cpu_set_t) -> io::Result<()> {
-        let res = unsafe { sched_getaffinity(pid, cpusetsize, mask) };
-        if res != 0 {
-            return Err(io::Error::last_os_error())
-        }
-        Ok(())
-    }
-}

+ 0 - 762
bin/minerd/src/hw/cpuid.rs

@@ -1,762 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{collections::HashMap, fmt};
-
-/// CPU Vendor
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Vendor {
-    Intel,
-    Amd,
-    Unknown,
-}
-
-impl fmt::Display for Vendor {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Vendor::Intel => write!(f, "Intel"),
-            Vendor::Amd => write!(f, "AMD"),
-            Vendor::Unknown => write!(f, "Unknown"),
-        }
-    }
-}
-
-/// AMD CPU microarchitecture generation
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum AmdZenGeneration {
-    /// Zen 1 (Ryzen 1000, EPYC Naples) - Family 17h, Models 00-0F
-    /// Zen+ (Ryzen 2000) - Family 17h, Models 10-1F
-    /// Zen 2 (Ryzen 3000, EPYC Rome) - Family 17h, Models 30-3F, 60-6F, 70-7F, 90-9F
-    Zen1OrZen2,
-
-    /// Zen 3 (Ryzen 5000, EPYC Milan) - Family 19h (25), various models
-    Zen3,
-
-    /// Zen 4 (Ryzen 7000, EPYC Genoa) - Family 19h (25), Models 61h (97), 75h (117)
-    Zen4,
-
-    /// Zen 5 (Ryzen 9000) - Family 1Ah (26)
-    Zen5,
-
-    /// Unknown or unsupported AMD CPU
-    Unknown,
-}
-
-impl fmt::Display for AmdZenGeneration {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            AmdZenGeneration::Zen1OrZen2 => write!(f, "Zen1/Zen2"),
-            AmdZenGeneration::Zen3 => write!(f, "Zen3"),
-            AmdZenGeneration::Zen4 => write!(f, "Zen4"),
-            AmdZenGeneration::Zen5 => write!(f, "Zen5"),
-            AmdZenGeneration::Unknown => write!(f, "Unknown AMD"),
-        }
-    }
-}
-
-/// Detected CPU information
-#[derive(Debug, Clone)]
-pub struct CpuInfo {
-    /// CPU vendor
-    pub vendor: Vendor,
-    /// Vendor string (e.g., "GenuineIntel", "AuthenticAMD")
-    pub vendor_string: String,
-    /// CPU family (after extended family calculation)
-    pub family: u32,
-    /// CPU model (after extended model calculation)
-    pub model: u32,
-    /// CPU stepping
-    pub stepping: u32,
-    /// Brand string (e.g., "AMD Ryzen 9 7950X")
-    pub brand_string: String,
-    /// AMD Zen generation (if AMD)
-    pub zen_generation: Option<AmdZenGeneration>,
-    /// CPU supports CAT L3
-    pub has_cat_l3: bool,
-}
-
-impl CpuInfo {
-    /// Detect the CPU using CPUID instruction
-    pub fn detect() -> Self {
-        let (vendor_string, vendor) = get_vendor();
-        let (family, model, stepping) = get_family_model_stepping();
-        let brand_string = get_brand_string();
-
-        let zen_generation =
-            if vendor == Vendor::Amd { Some(detect_zen_generation(family, model)) } else { None };
-
-        let has_cat_l3 = detect_cat_l3();
-
-        Self {
-            vendor,
-            vendor_string,
-            family,
-            model,
-            stepping,
-            brand_string,
-            zen_generation,
-            has_cat_l3,
-        }
-    }
-
-    pub fn threads(&self) -> CpuThreads {
-        CpuThreads::detect()
-    }
-
-    /// Check if this is an AMD CPU
-    pub fn is_amd(&self) -> bool {
-        self.vendor == Vendor::Amd
-    }
-
-    /// Check if this is an Intel CPU
-    pub fn is_intel(&self) -> bool {
-        self.vendor == Vendor::Intel
-    }
-
-    /// Check if this is an AMD Ryzen or EPYC CPU
-    pub fn is_ryzen_or_epyc(&self) -> bool {
-        self.is_amd() && (self.brand_string.contains("Ryzen") || self.brand_string.contains("EPYC"))
-    }
-}
-
-impl fmt::Display for CpuInfo {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        writeln!(f, "CPU Information:")?;
-        writeln!(f, "  Vendor: {} ({})", self.vendor, self.vendor_string)?;
-        writeln!(f, "  Family: {} (0x{:X})", self.family, self.family)?;
-        writeln!(f, "  Model: {} (0x{:X})", self.model, self.model)?;
-        writeln!(f, "  Stepping: {}", self.stepping)?;
-        if !self.brand_string.is_empty() {
-            writeln!(f, "  Brand: {}", self.brand_string)?;
-        }
-        if let Some(zen) = &self.zen_generation {
-            writeln!(f, "  Generation: {}", zen)?;
-        }
-        writeln!(f, "  CAT L3: {}", if self.has_cat_l3 { "supported" } else { "not supported" })?;
-        Ok(())
-    }
-}
-
-/// Execute CPUID instruction
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-fn cpuid(leaf: u32, subleaf: u32) -> (u32, u32, u32, u32) {
-    #[cfg(target_arch = "x86")]
-    use std::arch::x86::{CpuidResult, __cpuid_count};
-    #[cfg(target_arch = "x86_64")]
-    use std::arch::x86_64::{CpuidResult, __cpuid_count};
-
-    let result: CpuidResult = unsafe { __cpuid_count(leaf, subleaf) };
-    (result.eax, result.ebx, result.ecx, result.edx)
-}
-
-#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
-fn cpuid(_leaf: u32, _subleaf: u32) -> (u32, u32, u32, u32) {
-    (0, 0, 0, 0)
-}
-
-/// Get CPU vendor string and enum
-fn get_vendor() -> (String, Vendor) {
-    let (_, ebx, ecx, edx) = cpuid(0, 0);
-
-    // Vendor string is in EBX, EDX, ECX (in that order)
-    let vendor_bytes: [u8; 12] = [
-        (ebx & 0xFF) as u8,
-        ((ebx >> 8) & 0xFF) as u8,
-        ((ebx >> 16) & 0xFF) as u8,
-        ((ebx >> 24) & 0xFF) as u8,
-        (edx & 0xFF) as u8,
-        ((edx >> 8) & 0xFF) as u8,
-        ((edx >> 16) & 0xFF) as u8,
-        ((edx >> 24) & 0xFF) as u8,
-        (ecx & 0xFF) as u8,
-        ((ecx >> 8) & 0xFF) as u8,
-        ((ecx >> 16) & 0xFF) as u8,
-        ((ecx >> 24) & 0xFF) as u8,
-    ];
-
-    let vendor_string = String::from_utf8_lossy(&vendor_bytes).to_string();
-
-    let vendor = match vendor_string.as_str() {
-        "GenuineIntel" => Vendor::Intel,
-        "AuthenticAMD" => Vendor::Amd,
-        _ => Vendor::Unknown,
-    };
-
-    (vendor_string, vendor)
-}
-
-/// Get CPU family, model, and stepping from CPUID leaf 1
-fn get_family_model_stepping() -> (u32, u32, u32) {
-    let (eax, _, _, _) = cpuid(1, 0);
-
-    // Extract base values
-    let stepping = eax & 0xF;
-    let base_model = (eax >> 4) & 0xF;
-    let base_family = (eax >> 8) & 0xF;
-    let ext_model = (eax >> 16) & 0xF;
-    let ext_family = (eax >> 20) & 0xFF;
-
-    // Calculate actual family and model
-    // For family >= 0xF, add extended family
-    let family = if base_family == 0xF { base_family + ext_family } else { base_family };
-
-    // For family >= 0xF (AMD) or family == 0x6 (Intel), use extended model
-    let model = if base_family >= 0xF || base_family == 0x6 {
-        (ext_model << 4) | base_model
-    } else {
-        base_model
-    };
-
-    (family, model, stepping)
-}
-
-/// Get CPU brand string from CPUID leaves 0x80000002-0x80000004
-fn get_brand_string() -> String {
-    // Check if extended CPUID is supported
-    let (max_extended, _, _, _) = cpuid(0x80000000, 0);
-
-    if max_extended < 0x80000004 {
-        return String::new();
-    }
-
-    let mut brand_bytes = [0u8; 48];
-
-    for (i, leaf) in [0x80000002u32, 0x80000003, 0x80000004].iter().enumerate() {
-        let (eax, ebx, ecx, edx) = cpuid(*leaf, 0);
-        let offset = i * 16;
-
-        brand_bytes[offset..offset + 4].copy_from_slice(&eax.to_le_bytes());
-        brand_bytes[offset + 4..offset + 8].copy_from_slice(&ebx.to_le_bytes());
-        brand_bytes[offset + 8..offset + 12].copy_from_slice(&ecx.to_le_bytes());
-        brand_bytes[offset + 12..offset + 16].copy_from_slice(&edx.to_le_bytes());
-    }
-
-    // Convert to string and trim null bytes and whitespace
-    String::from_utf8_lossy(&brand_bytes)
-        .trim_matches(|c: char| c == '\0' || c.is_whitespace())
-        .to_string()
-}
-
-/// Detect AMD Zen generation based on family and model
-fn detect_zen_generation(family: u32, model: u32) -> AmdZenGeneration {
-    match family {
-        // Family 17h (23) = Zen, Zen+, Zen 2
-        0x17 => AmdZenGeneration::Zen1OrZen2,
-
-        // Family 19h (25) = Zen 3 or Zen 4
-        0x19 => {
-            // Zen 4 models: 0x61 (97), 0x75 (117)
-            // Also: 0x70-0x7F range for some Zen 4 parts
-            match model {
-                0x61 => AmdZenGeneration::Zen4,        // Raphael
-                0x75 => AmdZenGeneration::Zen4,        // Phoenix
-                0x10..=0x1F => AmdZenGeneration::Zen4, // Genoa
-                0x70..=0x7F => AmdZenGeneration::Zen4, // Dragon Range
-                0xA0..=0xAF => AmdZenGeneration::Zen4, // Phoenix 2
-                _ => AmdZenGeneration::Zen3,           // Other family 25 = Zen 3
-            }
-        }
-
-        // Family 1Ah (26) = Zen 5
-        0x1A => AmdZenGeneration::Zen5,
-
-        _ => AmdZenGeneration::Unknown,
-    }
-}
-
-/// Detect Cache Allocation Technology L3 support
-fn detect_cat_l3() -> bool {
-    let (max_leaf, _, _, _) = cpuid(0, 0);
-    if max_leaf < 7 {
-        return false;
-    }
-
-    let (_, ebx, _, _) = cpuid(7, 0);
-    let has_rdt_a = (ebx >> 15) & 1 != 0;
-
-    if !has_rdt_a {
-        return false;
-    }
-
-    if max_leaf < 0x10 {
-        return false;
-    }
-
-    let (_, ebx, _, _) = cpuid(0x10, 0);
-    (ebx >> 1) & 1 != 0
-}
-
-/// Get the number of logical CPUs
-pub fn get_cpu_count() -> usize {
-    #[cfg(target_os = "linux")]
-    {
-        std::fs::read_dir("/sys/devices/system/cpu")
-            .map(|entries| {
-                entries
-                    .filter_map(|e| e.ok())
-                    .filter(|e| {
-                        let name = e.file_name();
-                        let name = name.to_string_lossy();
-                        name.starts_with("cpu") &&
-                            name.chars().nth(3).map(|c| c.is_ascii_digit()).unwrap_or(false)
-                    })
-                    .count()
-            })
-            .unwrap_or(1)
-    }
-
-    #[cfg(target_os = "windows")]
-    {
-        std::env::var("NUMBER_OF_PROCESSORS").ok().and_then(|s| s.parse().ok()).unwrap_or(1)
-    }
-
-    #[cfg(not(any(target_os = "linux", target_os = "windows")))]
-    {
-        1
-    }
-}
-
-/// Get list of CPU unit IDs
-pub fn get_cpu_units() -> Vec<i32> {
-    (0..get_cpu_count() as i32).collect()
-}
-
-/// Information about a single CPU/logical processor
-#[derive(Debug, Clone)]
-pub struct CpuThread {
-    /// Logical processor ID (used for affinity)
-    pub id: i32,
-    /// Physical core ID this thread belongs to
-    pub core_id: i32,
-    /// Package/socket ID
-    pub package_id: i32,
-    /// NUMA node ID
-    pub node_id: i32,
-}
-
-/// CPU topology information
-#[derive(Debug, Clone)]
-pub struct CpuThreads {
-    /// All logical processors
-    threads: Vec<CpuThread>,
-    /// Number of physical packages/sockets
-    packages: u32,
-    /// Number of physical cores (total across all packages)
-    cores: u32,
-    /// Number of logical processors (threads)
-    logical: u32,
-    /// Number of NUMA nodes
-    nodes: u32,
-    /// L3 cache size in bytes (per package, 0 if unknown)
-    l3_cache_size: u64,
-    /// SMT/Hyperthreading support
-    smt_enabled: bool,
-}
-
-impl CpuThreads {
-    pub fn detect() -> Self {
-        #[cfg(target_os = "linux")]
-        {
-            Self::detect_linux()
-        }
-        #[cfg(target_os = "windows")]
-        {
-            Self::detect_windows()
-        }
-        #[cfg(not(any(target_os = "linux", target_os = "windows")))]
-        {
-            Self::detect_fallback()
-        }
-    }
-
-    pub fn threads(&self) -> &[CpuThread] {
-        &self.threads
-    }
-
-    pub fn thread_ids(&self) -> Vec<i32> {
-        self.threads.iter().map(|t| t.id).collect()
-    }
-
-    pub fn one_per_core(&self) -> Vec<i32> {
-        let mut seen_cores: HashMap<(i32, i32), i32> = HashMap::new();
-
-        for thread in &self.threads {
-            let key = (thread.package_id, thread.core_id);
-            seen_cores.entry(key).or_insert(thread.id);
-        }
-
-        let mut result: Vec<i32> = seen_cores.into_values().collect();
-        result.sort();
-        result
-    }
-
-    pub fn one_per_package(&self) -> Vec<i32> {
-        let mut seen_packages: HashMap<i32, i32> = HashMap::new();
-
-        for thread in &self.threads {
-            seen_packages.entry(thread.package_id).or_insert(thread.id);
-        }
-
-        let mut result: Vec<i32> = seen_packages.into_values().collect();
-        result.sort();
-        result
-    }
-
-    pub fn threads_for_package(&self, package_id: i32) -> Vec<i32> {
-        self.threads.iter().filter(|t| t.package_id == package_id).map(|t| t.id).collect()
-    }
-
-    pub fn threads_for_node(&self, node_id: i32) -> Vec<i32> {
-        self.threads.iter().filter(|t| t.node_id == node_id).map(|t| t.id).collect()
-    }
-
-    pub fn packages(&self) -> u32 {
-        self.packages
-    }
-
-    pub fn cores(&self) -> u32 {
-        self.cores
-    }
-
-    pub fn logical(&self) -> u32 {
-        self.logical
-    }
-
-    pub fn nodes(&self) -> u32 {
-        self.nodes
-    }
-
-    pub fn l3_cache_size(&self) -> u64 {
-        self.l3_cache_size
-    }
-
-    pub fn smt_enabled(&self) -> bool {
-        self.smt_enabled
-    }
-
-    pub fn threads_per_core(&self) -> u32 {
-        if self.cores > 0 {
-            self.logical / self.cores
-        } else {
-            1
-        }
-    }
-
-    #[cfg(target_os = "linux")]
-    fn detect_linux() -> Self {
-        let mut threads = Vec::new();
-        let mut max_package = 0i32;
-        let mut max_node = 0i32;
-        let mut core_set: std::collections::HashSet<(i32, i32)> = std::collections::HashSet::new();
-        let mut l3_cache_size = 0u64;
-
-        // Enumerate all CPUs
-        if let Ok(entries) = std::fs::read_dir("/sys/devices/system/cpu") {
-            let mut cpu_ids: Vec<i32> = entries
-                .filter_map(|e| e.ok())
-                .filter_map(|e| {
-                    let name = e.file_name();
-                    let name = name.to_string_lossy();
-                    if let Some(n) = name.strip_prefix("cpu") {
-                        n.parse::<i32>().ok()
-                    } else {
-                        None
-                    }
-                })
-                .collect();
-
-            cpu_ids.sort();
-
-            for cpu_id in cpu_ids {
-                let base_path = format!("/sys/devices/system/cpu/cpu{}", cpu_id);
-
-                // Check if this CPU is online (cpu0 is always online)
-                if cpu_id != 0 {
-                    let online_path = format!("{}/online", base_path);
-                    if let Ok(online) = std::fs::read_to_string(&online_path) {
-                        if online.trim() == "0" {
-                            continue; // Skip offline CPUs
-                        }
-                    }
-                }
-
-                let topology_path = format!("{}/topology", base_path);
-
-                let core_id = read_sysfs_int(&format!("{}/core_id", topology_path)).unwrap_or(0);
-                let package_id =
-                    read_sysfs_int(&format!("{}/physical_package_id", topology_path)).unwrap_or(0);
-
-                // NUMA node detection
-                let node_id = detect_numa_node(cpu_id);
-
-                max_package = max_package.max(package_id);
-                max_node = max_node.max(node_id);
-                core_set.insert((package_id, core_id));
-
-                threads.push(CpuThread { id: cpu_id, core_id, package_id, node_id });
-
-                // Get L3 cache size (once)
-                if l3_cache_size == 0 {
-                    l3_cache_size = detect_l3_cache_size(cpu_id);
-                }
-            }
-        }
-
-        let logical = threads.len() as u32;
-        let cores = core_set.len() as u32;
-        let packages = (max_package + 1) as u32;
-        let nodes = (max_node + 1) as u32;
-        let smt_enabled = logical > cores;
-
-        Self { threads, packages, cores, logical, nodes, l3_cache_size, smt_enabled }
-    }
-
-    #[cfg(target_os = "windows")]
-    fn detect_windows() -> Self {
-        use std::{mem, ptr};
-
-        // We'll use GetLogicalProcessorInformationEx for detailed topology
-        // For now, use a simpler approach with environment variables and CPUID
-
-        let logical = std::env::var("NUMBER_OF_PROCESSORS")
-            .ok()
-            .and_then(|s| s.parse::<u32>().ok())
-            .unwrap_or(1);
-
-        // Try to get more detailed info via Windows API
-        let (packages, cores, nodes, l3_cache_size) = get_windows_topology_info();
-
-        let smt_enabled = logical > cores;
-
-        // Build thread list (simplified - assumes sequential IDs)
-        let threads_per_core = if cores > 0 { logical / cores } else { 1 };
-        let cores_per_package = if packages > 0 { cores / packages } else { cores };
-
-        let mut threads = Vec::with_capacity(logical as usize);
-
-        for i in 0..logical {
-            let core_id = (i / threads_per_core) as i32;
-            let package_id = (core_id as u32 / cores_per_package) as i32;
-
-            threads.push(CpuThread {
-                id: i as i32,
-                core_id: core_id % cores_per_package as i32,
-                package_id,
-                node_id: package_id, // Simplified: assume 1 NUMA node per package
-            });
-        }
-
-        Self { threads, packages, cores, logical, nodes, l3_cache_size, smt_enabled }
-    }
-
-    #[cfg(not(any(target_os = "linux", target_os = "windows")))]
-    fn detect_fallback() -> Self {
-        let logical = 1u32;
-
-        Self {
-            threads: vec![CpuThread { id: 0, core_id: 0, package_id: 0, node_id: 0 }],
-            packages: 1,
-            cores: 1,
-            logical,
-            nodes: 1,
-            l3_cache_size: 0,
-            smt_enabled: false,
-        }
-    }
-}
-
-impl fmt::Display for CpuThreads {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        writeln!(f, "CPU Topology:")?;
-        writeln!(f, "  Packages: {}", self.packages)?;
-        writeln!(f, "  Cores: {}", self.cores)?;
-        writeln!(f, "  Threads: {}", self.logical)?;
-        writeln!(f, "  NUMA nodes: {}", self.nodes)?;
-        writeln!(f, "  SMT: {}", if self.smt_enabled { "enabled" } else { "disabled" })?;
-        if self.l3_cache_size > 0 {
-            writeln!(f, "  L3 Cache: {} MB", self.l3_cache_size / (1024 * 1024))?;
-        }
-        Ok(())
-    }
-}
-
-#[cfg(target_os = "linux")]
-fn read_sysfs_int(path: &str) -> Option<i32> {
-    std::fs::read_to_string(path).ok().and_then(|s| s.trim().parse().ok())
-}
-
-#[cfg(target_os = "linux")]
-fn detect_numa_node(cpu_id: i32) -> i32 {
-    // Try to find which NUMA node this CPU belongs to
-    if let Ok(entries) = std::fs::read_dir("/sys/devices/system/node") {
-        for entry in entries.filter_map(|e| e.ok()) {
-            let name = entry.file_name();
-            let name = name.to_string_lossy();
-            if let Some(n) = name.strip_prefix("node") {
-                if let Ok(node_id) = n.parse::<i32>() {
-                    let cpulist_path = format!("/sys/devices/system/node/{}/cpulist", name);
-                    if let Ok(cpulist) = std::fs::read_to_string(&cpulist_path) {
-                        if cpu_in_list(cpu_id, &cpulist) {
-                            return node_id;
-                        }
-                    }
-                }
-            }
-        }
-    }
-    0 // Default to node 0
-}
-
-#[cfg(target_os = "linux")]
-fn cpu_in_list(cpu_id: i32, list: &str) -> bool {
-    for part in list.trim().split(',') {
-        if part.contains('-') {
-            let range: Vec<&str> = part.split('-').collect();
-            if range.len() == 2 {
-                if let (Ok(start), Ok(end)) = (range[0].parse::<i32>(), range[1].parse::<i32>()) {
-                    if cpu_id >= start && cpu_id <= end {
-                        return true;
-                    }
-                }
-            }
-        } else if let Ok(id) = part.parse::<i32>() {
-            if id == cpu_id {
-                return true;
-            }
-        }
-    }
-    false
-}
-
-#[cfg(target_os = "linux")]
-fn detect_l3_cache_size(cpu_id: i32) -> u64 {
-    // Look through cache indices for L3
-    for index in 0..10 {
-        let cache_path = format!("/sys/devices/system/cpu/cpu{}/cache/index{}", cpu_id, index);
-
-        let level_path = format!("{}/level", cache_path);
-        let size_path = format!("{}/size", cache_path);
-
-        if let Ok(level) = std::fs::read_to_string(&level_path) {
-            if level.trim() == "3" {
-                if let Ok(size_str) = std::fs::read_to_string(&size_path) {
-                    return parse_cache_size(&size_str);
-                }
-            }
-        }
-    }
-    0
-}
-
-#[cfg(target_os = "linux")]
-fn parse_cache_size(size_str: &str) -> u64 {
-    let s = size_str.trim().to_uppercase();
-
-    if let Some(kb) = s.strip_suffix('K') {
-        kb.parse::<u64>().unwrap_or(0) * 1024
-    } else if let Some(mb) = s.strip_suffix('M') {
-        mb.parse::<u64>().unwrap_or(0) * 1024 * 1024
-    } else if let Some(gb) = s.strip_suffix('G') {
-        gb.parse::<u64>().unwrap_or(0) * 1024 * 1024 * 1024
-    } else {
-        s.parse::<u64>().unwrap_or(0)
-    }
-}
-
-// Windows helper functions
-#[cfg(target_os = "windows")]
-fn get_windows_topology_info() -> (u32, u32, u32, u64) {
-    /* UNTESTED:
-    use std::mem;
-    use windows::Win32::System::SystemInformation::{
-        GetLogicalProcessorInformation, RelationCache, RelationNumaNode, RelationProcessorCore,
-        RelationProcessorPackage, SYSTEM_LOGICAL_PROCESSOR_INFORMATION,
-    };
-
-    let mut packages = 1u32;
-    let mut cores = 0u32;
-    let mut nodes = 1u32;
-    let mut l3_cache_size = 0u64;
-
-    // Get required buffer size
-    let mut buffer_size = 0u32;
-    unsafe {
-        let _ = GetLogicalProcessorInformation(None, &mut buffer_size);
-    }
-
-    if buffer_size == 0 {
-        // Fallback to simple detection
-        let logical = std::env::var("NUMBER_OF_PROCESSORS")
-            .ok()
-            .and_then(|s| s.parse::<u32>().ok())
-            .unwrap_or(1);
-        return (1, logical, 1, 0);
-    }
-
-    let count = buffer_size as usize / mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>();
-    let mut buffer: Vec<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> =
-        vec![unsafe { mem::zeroed() }; count];
-
-    let result =
-        unsafe { GetLogicalProcessorInformation(Some(buffer.as_mut_ptr()), &mut buffer_size) };
-
-    if result.is_ok() {
-        let mut package_count = 0u32;
-        let mut numa_count = 0u32;
-
-        for info in &buffer {
-            match info.Relationship {
-                RelationProcessorCore => {
-                    cores += 1;
-                }
-                RelationProcessorPackage => {
-                    package_count += 1;
-                }
-                RelationNumaNode => {
-                    numa_count += 1;
-                }
-                RelationCache => {
-                    let cache = unsafe { info.Anonymous.Cache };
-                    if cache.Level == 3 && l3_cache_size == 0 {
-                        l3_cache_size = cache.Size as u64;
-                    }
-                }
-                _ => {}
-            }
-        }
-
-        if package_count > 0 {
-            packages = package_count;
-        }
-        if numa_count > 0 {
-            nodes = numa_count;
-        }
-    }
-
-    // If cores is still 0, use logical count
-    if cores == 0 {
-        cores = std::env::var("NUMBER_OF_PROCESSORS")
-            .ok()
-            .and_then(|s| s.parse::<u32>().ok())
-            .unwrap_or(1);
-    }
-
-    (packages, cores, nodes, l3_cache_size)
-    */
-    (0, 0, 0, 0)
-}

+ 0 - 203
bin/minerd/src/hw/mod.rs

@@ -1,203 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::collections::HashSet;
-
-use tracing::{debug, error, info, warn};
-
-use crate::cpu::CpuThread;
-
-/// CPU detection pub mod cpuid;
-pub mod cpuid;
-use cpuid::{CpuInfo, CpuThreads};
-
-/// Model-Specific Registers
-pub mod msr;
-use msr::{msr_presets, Msr, MsrItem, MsrPreset, NO_MASK};
-
-// MSR registers for Cache QoS
-const IA32_PQR_ASSOC: u32 = 0xC8F; // PQR (Platform QoS Resource) Association
-const IA32_L3_QOS_MASK_1: u32 = 0xC91; // L3 Cache QoS Mask for COS 1
-
-// Class of Service assignments
-const COS_FULL_CACHE: u64 = 0; // COS 0 = full L3 cache access
-const COS_LIMITED_CACHE: u64 = 1 << 32; // COS 1 = limited L3 cache (bit 32 sets COS in PQR_ASSOC)
-
-#[derive(Default)]
-pub struct RxMsr {
-    is_initialized: bool,
-    is_enabled: bool,
-    cache_qos: bool,
-    saved_items: Vec<MsrItem>,
-}
-
-impl RxMsr {
-    pub fn new() -> Self {
-        Self { is_initialized: false, is_enabled: false, cache_qos: false, saved_items: vec![] }
-    }
-
-    pub fn init(&mut self, cache_qos: bool, threads: &[CpuThread], save: bool) -> bool {
-        if self.is_initialized {
-            return self.is_enabled
-        }
-
-        self.is_initialized = true;
-        self.is_enabled = false;
-
-        // Detect CPU and find MSR preset
-        let cpu = CpuInfo::detect();
-        let msr_preset = if cpu.is_amd() && cpu.zen_generation.is_some() {
-            msr_presets::get_preset(MsrPreset::from_zen(cpu.zen_generation.unwrap()))
-        } else if cpu.is_intel() {
-            msr_presets::get_preset(MsrPreset::Intel)
-        } else {
-            msr_presets::get_preset(MsrPreset::None)
-        };
-
-        if msr_preset.is_empty() {
-            return false
-        }
-
-        self.cache_qos = cache_qos;
-        if self.cache_qos && !cpu.has_cat_l3 {
-            warn!("This CPU doesn't support cat_l3");
-            self.cache_qos = false;
-        }
-
-        self.is_enabled = self.wrmsr(msr_preset, threads, self.cache_qos, save);
-        if self.is_enabled {
-            info!("[msr] MSR register values set successfully");
-        } else {
-            error!("[msr] Failed to apply MSR mod, hashrate will be low");
-        }
-
-        self.is_enabled
-    }
-
-    pub fn destroy(&mut self) {
-        if !self.is_initialized {
-            return
-        }
-
-        self.is_initialized = false;
-        self.is_enabled = false;
-
-        if self.saved_items.is_empty() {
-            return
-        }
-
-        let saved_items = std::mem::take(&mut self.saved_items);
-
-        if !self.wrmsr(&saved_items, &[], self.cache_qos, false) {
-            error!("[msr] Failed to restore to initial state");
-        }
-    }
-
-    fn wrmsr(
-        &mut self,
-        preset: &[MsrItem],
-        threads: &[CpuThread],
-        cache_qos: bool,
-        save: bool,
-    ) -> bool {
-        let msr = Msr::get();
-        if msr.is_none() {
-            return false
-        }
-        let msr = msr.unwrap();
-
-        if save {
-            self.saved_items.reserve(preset.len());
-            for i in preset {
-                if let Some(item) = msr.read(i.reg(), -1, true) {
-                    if !item.is_valid() {
-                        self.saved_items.clear();
-                        return false
-                    }
-
-                    self.saved_items.push(item);
-                }
-            }
-        }
-
-        // Which CPU cores will have access top the full L3 cache
-        let mut cache_enabled: HashSet<i32> = HashSet::new();
-        let mut cache_qos_disabled = threads.is_empty();
-
-        if cache_qos {
-            let cpu_threads = CpuThreads::detect();
-            let units: HashSet<i32> = cpu_threads.thread_ids().into_iter().collect();
-
-            for thread in threads {
-                let affinity = thread.affinity();
-                // If some thread has no affinity or wrong affinity,
-                // disable cache QoS
-                if affinity < 0 || !units.contains(&affinity) {
-                    cache_qos_disabled = true;
-                    warn!(
-                        "Cache QoS can only be enabled when all mining threads have affinity set"
-                    );
-                    break
-                }
-                cache_enabled.insert(affinity);
-            }
-        }
-
-        // Apply MSR values to all CPUs
-        msr.write_all(|cpu| {
-            debug!("msr.write_all cpu={} get_cpu={}", cpu, get_cpu(cpu));
-            for item in preset {
-                if !msr.write(item.reg(), item.value(), get_cpu(cpu), item.mask(), true) {
-                    return false
-                }
-            }
-
-            if !cache_qos {
-                return true
-            }
-
-            // Cache QoS configuration
-            if cache_qos_disabled || cache_enabled.contains(&cpu) {
-                // Assign Class of Service 0 (full L3 cache) to this CPU
-                return msr.write(IA32_PQR_ASSOC, COS_FULL_CACHE, get_cpu(cpu), NO_MASK, true)
-            }
-
-            // For CPUs not running mining threads:
-            // Disable L3 cache for Class of Service 1
-            if !msr.write(IA32_L3_QOS_MASK_1, 0, get_cpu(cpu), NO_MASK, true) {
-                // Some CPUs don't allow setting it to all zeros
-                if !msr.write(IA32_L3_QOS_MASK_1, 1, get_cpu(cpu), NO_MASK, true) {
-                    return false
-                }
-            }
-
-            // Assign Class of Service 1 (limited cache) to this CPU
-            msr.write(IA32_PQR_ASSOC, COS_LIMITED_CACHE, get_cpu(cpu), NO_MASK, true)
-        })
-    }
-}
-
-#[cfg(target_os = "windows")]
-const fn get_cpu(cpu: i32) -> i32 {
-    -1
-}
-
-#[cfg(not(target_os = "windows"))]
-const fn get_cpu(cpu: i32) -> i32 {
-    cpu
-}

+ 0 - 85
bin/minerd/src/hw/msr/error.rs

@@ -1,85 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{fmt, io};
-
-pub type MsrResult<T> = Result<T, MsrError>;
-
-/// Errors that can occur during MSR operations
-#[derive(Debug)]
-pub enum MsrError {
-    /// MSR module/driver is not available
-    NotAvailable(String),
-
-    /// Failed to read MSR
-    ReadError { reg: u32, cpu: i32, source: io::Error },
-
-    /// Failed to write MSR
-    WriteError { reg: u32, cpu: i32, source: io::Error },
-
-    /// No CPU units available
-    NoCpuUnits,
-
-    /// Permission denied
-    PermissionDenied(String),
-
-    /// Driver installation failed (Windows)
-    DriverError(String),
-
-    /// Generic IO error
-    Io(io::Error),
-
-    /// Platform not supported
-    PlatformNotSupported,
-}
-
-impl fmt::Display for MsrError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            MsrError::NotAvailable(msg) => write!(f, "MSR not available: {}", msg),
-            MsrError::ReadError { reg, cpu, source } => {
-                write!(f, "Failed to read MSR 0x{:08x} on CPU {}: {}", reg, cpu, source)
-            }
-            MsrError::WriteError { reg, cpu, source } => {
-                write!(f, "Failed to write MSR 0x{:08x} on CPU {}: {}", reg, cpu, source)
-            }
-            MsrError::NoCpuUnits => write!(f, "No CPU units available"),
-            MsrError::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
-            MsrError::DriverError(msg) => write!(f, "Driver error: {}", msg),
-            MsrError::Io(err) => write!(f, "IO error: {}", err),
-            MsrError::PlatformNotSupported => write!(f, "Platform not supported"),
-        }
-    }
-}
-
-impl std::error::Error for MsrError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match self {
-            MsrError::ReadError { source, .. } => Some(source),
-            MsrError::WriteError { source, .. } => Some(source),
-            MsrError::Io(err) => Some(err),
-            _ => None,
-        }
-    }
-}
-
-impl From<io::Error> for MsrError {
-    fn from(err: io::Error) -> Self {
-        MsrError::Io(err)
-    }
-}

+ 0 - 244
bin/minerd/src/hw/msr/mod.rs

@@ -1,244 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-//! Cross-platform module for reading and writing CPU Model Specific Registers
-//!
-//! ## Platform support
-//!
-//! - Linux: Uses `/dev/cpu*/msr` interface with automatic module loading
-//! - Windows: Uses WinRing0 driver with automatic installation
-//!
-//! ## Requirements
-//!
-//! - Linux: root privileges and the `msr` kernel module
-//! - Windows: Administrator privileges and `WinRing0x64.sys`
-
-use std::sync::{Arc, Weak};
-
-use once_cell::sync::Lazy;
-use parking_lot::Mutex;
-
-use super::CpuThreads;
-
-mod error;
-mod msr_item;
-pub(super) mod msr_presets;
-
-use error::{MsrError, MsrResult};
-pub use msr_item::{MsrItem, NO_MASK};
-pub use msr_presets::MsrPreset;
-
-#[cfg(target_os = "linux")]
-mod msr_linux;
-
-/*
-#[cfg(target_os = "windows")]
-mod msr_win;
-*/
-
-#[cfg(target_os = "linux")]
-use msr_linux::MsrImpl;
-
-#[cfg(not(any(target_os = "linux")))]
-use unsupported::MsrImpl;
-
-/// Global weak reference to the MSR singleton
-static INSTANCE: Lazy<Mutex<Weak<Msr>>> = Lazy::new(|| Mutex::new(Weak::new()));
-
-/// MSR Interface
-pub struct Msr {
-    inner: MsrImpl,
-}
-
-impl Msr {
-    /// Get or create the MSR singleton
-    ///
-    /// Returns `None` if MSR is not available on this system.
-    pub fn get() -> Option<Arc<Self>> {
-        let mut instance = INSTANCE.lock();
-
-        // Try to upgrade the weak reference
-        if let Some(msr) = instance.upgrade() {
-            if msr.is_available() {
-                return Some(msr);
-            }
-        }
-
-        // Autodetect CPU units
-        let units = CpuThreads::detect().thread_ids();
-
-        let msr = Arc::new(Self { inner: MsrImpl::new(units) });
-
-        if msr.is_available() {
-            *instance = Arc::downgrade(&msr);
-            Some(msr)
-        } else {
-            None
-        }
-    }
-
-    /// Get or create the MSR singleton with specific CPU units
-    ///
-    /// Returns `None` if MSR is not available on this system.
-    pub fn get_with_units(units: Vec<i32>) -> Option<Arc<Self>> {
-        let mut instance = INSTANCE.lock();
-
-        // Try to upgrade the weak reference
-        if let Some(msr) = instance.upgrade() {
-            if msr.is_available() {
-                return Some(msr);
-            }
-        }
-
-        // Create new instance with provided units
-        let msr = Arc::new(Self { inner: MsrImpl::new(units) });
-
-        if msr.is_available() {
-            *instance = Arc::downgrade(&msr);
-            Some(msr)
-        } else {
-            None
-        }
-    }
-
-    /// Create a new MSR instance without using the singleton.
-    ///
-    /// Useful for testing or when multiple independent instances are needed.
-    pub fn new_instance(units: Vec<i32>) -> Self {
-        Self { inner: MsrImpl::new(units) }
-    }
-
-    /// Check if MSR operations are available
-    pub fn is_available(&self) -> bool {
-        self.inner.is_available()
-    }
-
-    /// Get the CPU units this MSR operates on
-    pub fn units(&self) -> &[i32] {
-        self.inner.units()
-    }
-
-    /// Write an MsrItem to a CPU
-    ///
-    /// * `item`: The MSR item to write
-    /// * `cpu`: CPU index (-1 for default/first)
-    /// * `verbose`: Log warnings on failure
-    pub fn write_item(&self, item: &MsrItem, cpu: i32, verbose: bool) -> bool {
-        self.inner.write_item(item, cpu, verbose)
-    }
-
-    /// Write to an MSR register with optional mask
-    ///
-    /// If a mask is provided, this performs a read-modify-write
-    ///
-    /// * `reg`: MSR register address
-    /// * `value`: Value to write
-    /// * `cpu`: CPU index (-1 for default/first)
-    /// * `mask`: Bit mask for partial updates (use NO_MASK for full write)
-    /// * `verbose`: Log warnings on failure
-    pub fn write(&self, reg: u32, value: u64, cpu: i32, mask: u64, verbose: bool) -> bool {
-        self.inner.write(reg, value, cpu, mask, verbose)
-    }
-
-    /// Execute a callback for each CPU unit
-    ///
-    /// The callback receives the CPU ID and should return `true` to continue
-    /// or `false` to abort.
-    pub fn write_all<F>(&self, callback: F) -> bool
-    where
-        F: FnMut(i32) -> bool + Send,
-    {
-        self.inner.write_all(callback)
-    }
-
-    /// Read an MSR register and return as MsrItem
-    ///
-    /// * `reg`: MSR register address
-    /// * `cpu`: CPU index (-1 for default/first)
-    /// * `verbose`: Log warnings on failure
-    pub fn read(&self, reg: u32, cpu: i32, verbose: bool) -> Option<MsrItem> {
-        self.inner.read(reg, cpu, verbose)
-    }
-
-    /// Low-level MSR read
-    ///
-    /// Returns the raw value or error
-    pub fn rdmsr(&self, reg: u32, cpu: i32) -> MsrResult<u64> {
-        self.inner.rdmsr(reg, cpu)
-    }
-
-    /// Low-level MSR write
-    ///
-    /// Writes the value directly without masking
-    pub fn wrmsr(&self, reg: u32, value: u64, cpu: i32) -> MsrResult<()> {
-        self.inner.wrmsr(reg, value, cpu)
-    }
-}
-
-/// Impls for unsupported platforms.
-#[cfg(not(any(target_os = "linux")))]
-mod unsupported {
-    use super::*;
-    use tracing::warn;
-
-    pub struct MsrImpl {
-        units: Vec<i32>,
-    }
-
-    impl MsrImpl {
-        pub fn new(units: Vec<i32>) -> Self {
-            warn!("[msr] MSR not supported on this platform");
-            Self { units }
-        }
-
-        pub fn is_available(&self) -> bool {
-            false
-        }
-
-        pub fn units(&self) -> &[i32] {
-            &self.units
-        }
-
-        pub fn write_all<F>(&self, _callback: F) -> bool
-        where
-            F: FnMut(i32) -> bool,
-        {
-            false
-        }
-
-        pub fn rdmsr(&self, reg: u32, cpu: i32) -> MsrResult<u64> {
-            Err(crate::error::MsrError::PlatformNotSupported)
-        }
-
-        pub fn wrmsr(&self, _reg: u32, _value: u64, _cpu: i32) -> MsrResult<()> {
-            Err(crate::error::MsrError::PlatformNotSupported)
-        }
-
-        pub fn write(&self, _reg: u32, _value: u64, _cpu: i32, _mask: u64, _verbose: bool) -> bool {
-            false
-        }
-
-        pub fn write_item(&self, _item: &MsrItem, _cpu: i32, _verbose: bool) -> bool {
-            false
-        }
-
-        pub fn read(&self, _reg: u32, _cpu: i32, _verbose: bool) -> Option<MsrItem> {
-            None
-        }
-    }
-}

+ 0 - 175
bin/minerd/src/hw/msr/msr_item.rs

@@ -1,175 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{fmt, str::FromStr};
-
-/// Sentinel value indicating no mask should be applied
-pub const NO_MASK: u64 = u64::MAX;
-
-/// Represents a single MSR operation.
-///
-/// Contains the register address, value to write/read, and an optional
-/// mask for partial register updates.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct MsrItem {
-    /// The MSR register address
-    reg: u32,
-    /// The value to read/write
-    value: u64,
-    /// Mask for partial updates (`NO_MASK` means full update)
-    mask: u64,
-}
-
-impl Default for MsrItem {
-    fn default() -> Self {
-        Self { reg: 0, value: 0, mask: NO_MASK }
-    }
-}
-
-impl MsrItem {
-    /// Create a new MsrItem with optional mask
-    pub const fn new(reg: u32, value: u64) -> Self {
-        Self { reg, value, mask: NO_MASK }
-    }
-
-    /// Create a new MsrItem with a specific mask
-    pub const fn with_mask(reg: u32, value: u64, mask: u64) -> Self {
-        Self { reg, value, mask }
-    }
-
-    /// Check if this item is valid (register > 0)
-    #[inline]
-    pub const fn is_valid(&self) -> bool {
-        self.reg > 0
-    }
-
-    /// Get the register address
-    #[inline]
-    pub const fn reg(&self) -> u32 {
-        self.reg
-    }
-
-    /// Get the value
-    #[inline]
-    pub const fn value(&self) -> u64 {
-        self.value
-    }
-
-    /// Get the mask
-    #[inline]
-    pub const fn mask(&self) -> u64 {
-        self.mask
-    }
-
-    /// Check if this item has a mask
-    #[inline]
-    pub const fn has_mask(&self) -> bool {
-        self.mask != NO_MASK
-    }
-
-    /// Apply mask to combine old and new values.
-    ///
-    /// The masked bits from `new_value` replace the corresponding bits in
-    /// `old_value`, while unmasked bits retain the `old_value`.
-    ///
-    /// ```text
-    /// let old = 0xFF00_FF00;
-    /// let new = 0x1234_5678;
-    /// let mask = 0xFFFF_0000;
-    ///
-    /// // Upper 16 bits from new, lower 16 bits from old:
-    /// assert_eq!(MsrItem::masked_value(old, new, mask), 0x1234_FF00);
-    /// ```
-    #[inline]
-    pub const fn masked_value(old_value: u64, new_value: u64, mask: u64) -> u64 {
-        (new_value & mask) | (old_value & !mask)
-    }
-
-    /// Set the value, useful for updating after a read
-    #[inline]
-    pub fn set_value(&mut self, value: u64) {
-        self.value = value;
-    }
-}
-
-/// Error type for parsing MsrItem from a string
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct ParseMsrItemError {
-    pub message: String,
-}
-
-impl fmt::Display for ParseMsrItemError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "Failed to parse MsrItem: {}", self.message)
-    }
-}
-
-impl std::error::Error for ParseMsrItemError {}
-
-impl FromStr for MsrItem {
-    type Err = ParseMsrItemError;
-
-    /// Parse an MsrItem from a string in format `REG:VALUE` or `REG:VALUE:MASK`
-    ///
-    /// Values can be decimal or hexadecimal.
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let parts: Vec<&str> = s.split(':').collect();
-
-        if parts.len() < 2 {
-            return Err(ParseMsrItemError {
-                message: "Expected format REG:VALUE or REG:VALUE:MASK".to_string(),
-            })
-        }
-
-        let reg = parse_number(parts[0])
-            .map_err(|e| ParseMsrItemError { message: format!("Invalid register: {}", e) })?
-            as u32;
-
-        let value = parse_number(parts[1])
-            .map_err(|e| ParseMsrItemError { message: format!("Invalid value: {}", e) })?;
-
-        let mask = if parts.len() > 2 {
-            parse_number(parts[2])
-                .map_err(|e| ParseMsrItemError { message: format!("Invalid mask: {}", e) })?
-        } else {
-            NO_MASK
-        };
-
-        Ok(Self { reg, value, mask })
-    }
-}
-
-/// Parse a number from string, supporting decimal and hexadecimal
-fn parse_number(s: &str) -> Result<u64, std::num::ParseIntError> {
-    let s = s.trim();
-    if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
-        u64::from_str_radix(hex, 16)
-    } else {
-        s.parse::<u64>()
-    }
-}
-
-impl fmt::Display for MsrItem {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.mask != NO_MASK {
-            write!(f, "0x{:x}:0x{:x}:0x{:x}", self.reg, self.value, self.mask)
-        } else {
-            write!(f, "0x{:x}:0x{:x}", self.reg, self.value)
-        }
-    }
-}

+ 0 - 197
bin/minerd/src/hw/msr/msr_linux.rs

@@ -1,197 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-//! Linux-specific MSR implementation using `/dev/cpu/*/msr`
-
-use std::{
-    fs::{File, OpenOptions},
-    io::{self, Write},
-    os::fd::AsRawFd,
-    process::Command,
-};
-
-use tracing::warn;
-
-use super::{msr_item::NO_MASK, MsrError, MsrItem, MsrResult};
-
-pub struct MsrImpl {
-    available: bool,
-    units: Vec<i32>,
-}
-
-impl MsrImpl {
-    /// Create a new Linux MSR interface
-    pub fn new(units: Vec<i32>) -> Self {
-        let available = Self::msr_allow_writes() || Self::msr_modprobe();
-
-        if !available {
-            warn!("[msr] MSR kernel module not available");
-        }
-
-        Self { available, units }
-    }
-
-    /// Check if MSR operations are available
-    pub fn is_available(&self) -> bool {
-        self.available
-    }
-
-    /// Get the CPU units
-    pub fn units(&self) -> &[i32] {
-        &self.units
-    }
-
-    /// Execute a callback for each CPU unit
-    pub fn write_all<F>(&self, mut callback: F) -> bool
-    where
-        F: FnMut(i32) -> bool,
-    {
-        for &cpu in &self.units {
-            if !callback(cpu) {
-                return false;
-            }
-        }
-        true
-    }
-
-    /// Read an MSR register
-    pub fn rdmsr(&self, reg: u32, cpu: i32) -> MsrResult<u64> {
-        let cpu_id = self.resolve_cpu(cpu)?;
-        let path = format!("/dev/cpu/{}/msr", cpu_id);
-
-        let file =
-            File::open(&path).map_err(|e| MsrError::ReadError { reg, cpu: cpu_id, source: e })?;
-
-        let mut value = 0u64;
-        let bytes_read = unsafe {
-            libc::pread(
-                file.as_raw_fd(),
-                &mut value as *mut u64 as *mut libc::c_void,
-                std::mem::size_of::<u64>(),
-                reg as libc::off_t,
-            )
-        };
-
-        if bytes_read == std::mem::size_of::<u64>() as isize {
-            Ok(value)
-        } else {
-            Err(MsrError::ReadError { reg, cpu: cpu_id, source: io::Error::last_os_error() })
-        }
-    }
-
-    /// Write an MSR register
-    pub fn wrmsr(&self, reg: u32, value: u64, cpu: i32) -> MsrResult<()> {
-        let cpu_id = self.resolve_cpu(cpu)?;
-        let path = format!("/dev/cpu/{}/msr", cpu_id);
-
-        let file = OpenOptions::new()
-            .write(true)
-            .open(&path)
-            .map_err(|e| MsrError::WriteError { reg, cpu: cpu_id, source: e })?;
-
-        let bytes_written = unsafe {
-            libc::pwrite(
-                file.as_raw_fd(),
-                &value as *const u64 as *const libc::c_void,
-                std::mem::size_of::<u64>(),
-                reg as libc::off_t,
-            )
-        };
-
-        if bytes_written == std::mem::size_of::<u64>() as isize {
-            Ok(())
-        } else {
-            Err(MsrError::WriteError { reg, cpu: cpu_id, source: io::Error::last_os_error() })
-        }
-    }
-
-    /// Write MSR with mask support (read-modify-write)
-    pub fn write(&self, reg: u32, value: u64, cpu: i32, mask: u64, verbose: bool) -> bool {
-        let write_value = if mask != NO_MASK {
-            match self.rdmsr(reg, cpu) {
-                Ok(old_value) => MsrItem::masked_value(old_value, value, mask),
-                Err(e) => {
-                    if verbose {
-                        warn!("[msr] Cannot read MSR 0x{:08x}: {}", reg, e);
-                    }
-                    return false
-                }
-            }
-        } else {
-            value
-        };
-
-        match self.wrmsr(reg, write_value, cpu) {
-            Ok(()) => true,
-            Err(e) => {
-                if verbose {
-                    warn!("[msr] Cannot set MSR 0x{:08x} to 0x{:016x}: {}", reg, write_value, e);
-                }
-                false
-            }
-        }
-    }
-
-    /// Write an MsrItem
-    pub fn write_item(&self, item: &MsrItem, cpu: i32, verbose: bool) -> bool {
-        self.write(item.reg(), item.value(), cpu, item.mask(), verbose)
-    }
-
-    /// Read MSR and return as MsrItem
-    pub fn read(&self, reg: u32, cpu: i32, verbose: bool) -> Option<MsrItem> {
-        match self.rdmsr(reg, cpu) {
-            Ok(value) => Some(MsrItem::new(reg, value)),
-            Err(e) => {
-                if verbose {
-                    warn!("[msr] Cannot read MSR 0x{:08x}: {}", reg, e);
-                }
-                None
-            }
-        }
-    }
-
-    /// Resolve CPU index (-1 means use first available)
-    fn resolve_cpu(&self, cpu: i32) -> MsrResult<i32> {
-        if cpu < 0 {
-            self.units.first().copied().ok_or(MsrError::NoCpuUnits)
-        } else {
-            Ok(cpu)
-        }
-    }
-
-    /// Try to enable MSR writes via sysfs
-    fn msr_allow_writes() -> bool {
-        OpenOptions::new()
-            .write(true)
-            .truncate(true)
-            .open("/sys/module/msr/parameters/allow_writes")
-            .and_then(|mut file| file.write_all(b"on"))
-            .is_ok()
-    }
-
-    /// Try to load MSR module via modprobe
-    fn msr_modprobe() -> bool {
-        Command::new("/sbin/modprobe")
-            .args(["msr", "allow_writes=on"])
-            .stdout(std::process::Stdio::null())
-            .stderr(std::process::Stdio::null())
-            .status()
-            .map(|status| status.success())
-            .unwrap_or(false)
-    }
-}

+ 0 - 105
bin/minerd/src/hw/msr/msr_presets.rs

@@ -1,105 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use super::{super::cpuid::AmdZenGeneration, MsrItem};
-
-/// MSR preset index for supported CPU types
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[repr(usize)]
-pub enum MsrPreset {
-    /// No preset (empty)
-    None = 0,
-    /// AMD Zen1 / Zen+ / Zen2
-    Zen1Zen2 = 1,
-    /// AMD Zen 3
-    Zen3 = 2,
-    /// AMD Zen 4
-    Zen4 = 3,
-    /// AMD Zen 5
-    Zen5 = 4,
-    /// Intel
-    Intel = 5,
-    /// Maximum value
-    Max = 6,
-}
-
-impl MsrPreset {
-    /// Get preset from AMD Zen generation
-    pub fn from_zen(gen: AmdZenGeneration) -> Self {
-        match gen {
-            AmdZenGeneration::Zen1OrZen2 => MsrPreset::Zen1Zen2,
-            AmdZenGeneration::Zen3 => MsrPreset::Zen3,
-            AmdZenGeneration::Zen4 => MsrPreset::Zen4,
-            AmdZenGeneration::Zen5 => MsrPreset::Zen5,
-            AmdZenGeneration::Unknown => MsrPreset::None,
-        }
-    }
-}
-
-/// Total number of MSR presets
-pub const MSR_ARRAY_SIZE: usize = MsrPreset::Max as usize + 1;
-
-/// Mask that clears bit 5 (used for 0xC0011021 register)
-/// This is `~0x20ULL` in C++
-const MASK_CLEAR_BIT5: u64 = !0x20u64; // 0xFFFFFFFFFFFFFFDF
-
-/// Static array of MSR presets for each CPU type
-///
-/// Index corresponds to `MsrPreset` enum values.
-pub static MSR_PRESETS: [&[MsrItem]; MSR_ARRAY_SIZE] = [
-    // 0: None - empty preset
-    &[],
-    // 1: Zen1/Zen2
-    &[
-        MsrItem::new(0xc0011020, 0x0),
-        MsrItem::with_mask(0xc0011021, 0x40, MASK_CLEAR_BIT5),
-        MsrItem::new(0xc0011022, 0x1510000),
-        MsrItem::new(0xc001102b, 0x2000cc16),
-    ],
-    // 2: Zen3
-    &[
-        MsrItem::new(0xc0011020, 0x0004480000000000),
-        MsrItem::with_mask(0xc0011021, 0x001c000200000040, MASK_CLEAR_BIT5),
-        MsrItem::new(0xc0011022, 0xc000000401570000),
-        MsrItem::new(0xc001102b, 0x2000cc10),
-    ],
-    // 3: Zen4
-    &[
-        MsrItem::new(0xc0011020, 0x0004400000000000),
-        MsrItem::with_mask(0xc0011021, 0x0004000000000040, MASK_CLEAR_BIT5),
-        MsrItem::new(0xc0011022, 0x8680000401570000),
-        MsrItem::new(0xc001102b, 0x2040cc10),
-    ],
-    // 4: Zen5
-    &[
-        MsrItem::new(0xc0011020, 0x0004400000000000),
-        MsrItem::with_mask(0xc0011021, 0x0004000000000040, MASK_CLEAR_BIT5),
-        MsrItem::new(0xc0011022, 0x8680000401570000),
-        MsrItem::new(0xc001102b, 0x2040cc10),
-    ],
-    // 5: Intel
-    &[MsrItem::new(0x1a4, 0xf)],
-    // 6: Max - empty sentinel
-    &[],
-];
-
-/// Get MSR items for a specific preset
-#[inline]
-pub fn get_preset(preset: MsrPreset) -> &'static [MsrItem] {
-    MSR_PRESETS[preset as usize]
-}

+ 0 - 311
bin/minerd/src/lib.rs

@@ -1,311 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{collections::HashMap, sync::Arc};
-
-use smol::{
-    channel::{Receiver, Sender},
-    lock::RwLock,
-};
-use tracing::{debug, error, info};
-use url::Url;
-
-use darkfi::{
-    rpc::util::JsonValue,
-    system::{sleep, ExecutorPtr, StoppableTask, StoppableTaskPtr},
-    Error,
-};
-use darkfi_sdk::crypto::keypair::{Address, Keypair, Network, StandardAddress};
-
-/// Miner benchmarking related methods
-pub mod benchmark;
-
-/// Hardware interfaces
-pub mod hw;
-
-/// CPU information
-pub mod cpu;
-
-/// darkfid JSON-RPC related methods
-mod rpc;
-use rpc::{polling_task, DarkfidRpcClient};
-
-/// Auxiliary structure representing miner node configuration.
-pub struct MinerNodeConfig {
-    /// Flag indicating whether to mine in fast mode
-    fast_mode: bool,
-    /// Flag indicating whether to mine with Large Pages
-    large_pages: bool,
-    /// Flag indicating whether to mine with secure access to JIT memory (if supported)
-    secure: bool,
-    /// PoW miner number of threads to use
-    threads: usize,
-    /// Polling rate to ask darkfid for mining jobs
-    polling_rate: u64,
-    /// Stop mining at this height (0 mines forever)
-    stop_at_height: u32,
-    /// Wallet mining configuration to receive mining rewards
-    wallet_config: HashMap<String, JsonValue>,
-}
-
-impl Default for MinerNodeConfig {
-    fn default() -> Self {
-        let address: Address =
-            StandardAddress::from_public(Network::Mainnet, Keypair::default().public).into();
-        Self::new(
-            true,
-            false,
-            false,
-            1,
-            5,
-            0,
-            HashMap::from([(String::from("recipient"), JsonValue::String(address.to_string()))]),
-        )
-    }
-}
-
-impl MinerNodeConfig {
-    pub fn new(
-        fast_mode: bool,
-        large_pages: bool,
-        secure: bool,
-        threads: usize,
-        polling_rate: u64,
-        stop_at_height: u32,
-        wallet_config: HashMap<String, JsonValue>,
-    ) -> Self {
-        Self {
-            fast_mode,
-            large_pages,
-            secure,
-            threads,
-            polling_rate,
-            stop_at_height,
-            wallet_config,
-        }
-    }
-}
-
-/// Atomic pointer to the DarkFi mining node
-pub type MinerNodePtr = Arc<MinerNode>;
-
-/// Structure representing a DarkFi mining node
-pub struct MinerNode {
-    /// Node configuration
-    config: MinerNodeConfig,
-    /// Sender and receiver to stop mining threads
-    mining_channel: (Sender<()>, Receiver<()>),
-    /// Sender and receiver to stop background threads
-    background_channel: (Sender<()>, Receiver<()>),
-    /// JSON-RPC client to execute requests to darkfid daemon
-    rpc_client: RwLock<DarkfidRpcClient>,
-}
-
-impl MinerNode {
-    pub async fn new(config: MinerNodeConfig, endpoint: Url, ex: &ExecutorPtr) -> MinerNodePtr {
-        // Initialize the smol channels to send signal between the threads
-        let mining_channel = smol::channel::bounded(1);
-        let background_channel = smol::channel::bounded(1);
-
-        // Initialize JSON-RPC client
-        let rpc_client = RwLock::new(DarkfidRpcClient::new(endpoint, ex.clone()).await);
-
-        Arc::new(Self { config, mining_channel, background_channel, rpc_client })
-    }
-
-    /// Auxiliary function to abort all pending tasks.
-    pub async fn abort(&self) {
-        self.abort_mining().await;
-        self.abort_background().await;
-    }
-
-    /// Auxiliary function to abort pending mining task.
-    pub async fn abort_mining(&self) {
-        Self::abort_task(&self.mining_channel.0, &self.mining_channel.1, "mining").await;
-    }
-
-    /// Auxiliary function to abort pending background Randomx VMs
-    /// generation task.
-    pub async fn abort_background(&self) {
-        Self::abort_task(&self.background_channel.0, &self.background_channel.1, "VMs generation")
-            .await;
-    }
-
-    /// Auxiliary function to abort pending task by signaling provided
-    /// channels.
-    async fn abort_task(sender: &Sender<()>, stop_signal: &Receiver<()>, task: &str) {
-        // Check if a pending task is being processed
-        debug!(target: "minerd::abort_task", "Checking if a pending {task} task is being processed...");
-        if stop_signal.receiver_count() <= 1 {
-            debug!(target: "minerd::abort_task", "No pending {task} task!");
-            return
-        }
-
-        info!(target: "minerd::abort_task", "Pending {task} is in progress, sending stop signal...");
-        // Send stop signal to worker
-        if let Err(e) = sender.try_send(()) {
-            error!(target: "minerd::abort_task", "Failed to stop pending {task} task: {e}");
-            return
-        }
-
-        // Wait for worker to terminate
-        info!(target: "minerd::abort_task", "Waiting for {task} task to terminate...");
-        while stop_signal.receiver_count() > 1 {
-            sleep(1).await;
-        }
-        info!(target: "minerd::abort_task", "Pending {task} task terminated!");
-
-        // Consume channel item so its empty again
-        if let Err(e) = stop_signal.try_recv() {
-            error!(target: "minerd::abort_task", "Failed to cleanup stop signal channel: {e}");
-        }
-    }
-}
-
-/// Atomic pointer to the DarkFi mining daemon
-pub type MinerdPtr = Arc<Minerd>;
-
-/// Structure representing a DarkFi mining daemon
-pub struct Minerd {
-    /// Miner node instance conducting the mining operations
-    node: MinerNodePtr,
-    /// Miner darkfid polling background task
-    polling_task: StoppableTaskPtr,
-}
-
-impl Minerd {
-    /// Initialize a DarkFi mining daemon.
-    ///
-    /// Generate a new `MinerNode` and a new task to handle the darkfid
-    /// polling.
-    pub async fn init(config: MinerNodeConfig, endpoint: Url, ex: &ExecutorPtr) -> MinerdPtr {
-        info!(target: "minerd::Minerd::init", "Initializing a new mining daemon...");
-
-        // Generate the node
-        let node = MinerNode::new(config, endpoint, ex).await;
-
-        // Generate the polling task
-        let polling_task = StoppableTask::new();
-
-        info!(target: "minerd::Minerd::init", "Mining daemon initialized successfully!");
-
-        Arc::new(Self { node, polling_task })
-    }
-
-    /// Start the DarkFi mining daemon in the given executor.
-    pub fn start(&self, ex: &ExecutorPtr) {
-        info!(target: "minerd::Minerd::start", "Starting mining daemon...");
-
-        // Start the polling task
-        self.polling_task.clone().start(
-            polling_task(self.node.clone(), ex.clone()),
-            |res| async {
-                match res {
-                    Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
-                    Err(e) => {
-                        error!(target: "minerd::Minerd::start", "Failed starting polling task: {e}")
-                    }
-                }
-            },
-            Error::DetachedTaskStopped,
-            ex.clone(),
-        );
-
-        info!(target: "minerd::Minerd::start", "Mining daemon started successfully!");
-    }
-
-    /// Stop the DarkFi mining daemon.
-    pub async fn stop(&self) {
-        info!(target: "minerd::Minerd::stop", "Terminating mining daemon...");
-
-        // Stop the mining node
-        info!(target: "minerd::Minerd::stop", "Stopping miner background tasks...");
-        self.node.abort().await;
-
-        // Stop the polling task
-        info!(target: "minerd::Minerd::stop", "Stopping polling task...");
-        self.polling_task.stop().await;
-
-        // Close the JSON-RPC client
-        info!(target: "minerd::Minerd::stop", "Stopping JSON-RPC client...");
-        self.node.stop_rpc_client().await;
-
-        info!(target: "minerd::Minerd::stop", "Mining daemon terminated successfully!");
-    }
-}
-
-#[cfg(test)]
-use {
-    darkfi::util::logger::{setup_test_logger, Level},
-    tracing::warn,
-};
-
-#[test]
-/// Test the programmatic control of `Minerd`.
-///
-/// First we initialize a daemon, start it and then perform
-/// couple of restarts to verify everything works as expected.
-fn minerd_programmatic_control() {
-    // We check this error so we can execute same file tests in parallel,
-    // otherwise second one fails to init logger here.
-    if setup_test_logger(
-        &[],
-        false,
-        Level::Info,
-        //Level::Verbose,
-        //Level::Debug,
-        //Level::Trace,
-    )
-    .is_err()
-    {
-        warn!(target: "minerd_programmatic_control", "Logger already initialized");
-    }
-
-    // Create an executor and communication signals
-    let ex = Arc::new(smol::Executor::new());
-    let (signal, shutdown) = smol::channel::unbounded::<()>();
-
-    easy_parallel::Parallel::new().each(0..1, |_| smol::block_on(ex.run(shutdown.recv()))).finish(
-        || {
-            smol::block_on(async {
-                // Initialize a daemon
-                let daemon = Minerd::init(
-                    MinerNodeConfig::default(),
-                    Url::parse("tcp://127.0.0.1:12345").unwrap(),
-                    &ex,
-                )
-                .await;
-
-                // Start it
-                daemon.start(&ex);
-
-                // Stop it
-                daemon.stop().await;
-
-                // Start it again
-                daemon.start(&ex);
-
-                // Stop it
-                daemon.stop().await;
-
-                // Shutdown entirely
-                drop(signal);
-            })
-        },
-    );
-}

+ 0 - 263
bin/minerd/src/main.rs

@@ -1,263 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{collections::HashMap, str::FromStr};
-
-use smol::{fs::read_to_string, stream::StreamExt};
-use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
-use tracing::{debug, error, info};
-use url::Url;
-
-use darkfi::{
-    async_daemonize, cli_desc, rpc::util::JsonValue, system::ExecutorPtr,
-    util::path::get_config_path, Error, Result,
-};
-use darkfi_sdk::{
-    crypto::{keypair::Address, pasta_prelude::PrimeField, FuncId},
-    pasta::pallas,
-};
-
-use minerd::{
-    benchmark::benchmark,
-    cpu::CpuThreads,
-    hw::{cpuid::CpuInfo, RxMsr},
-    MinerNodeConfig, Minerd,
-};
-
-const CONFIG_FILE: &str = "minerd.toml";
-const CONFIG_FILE_CONTENTS: &str = include_str!("../minerd.toml");
-
-#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
-#[serde(default)]
-#[structopt(name = "minerd", about = cli_desc!())]
-struct Args {
-    #[structopt(short, long)]
-    /// Configuration file to use
-    config: Option<String>,
-
-    #[structopt(short, long)]
-    /// Number of nonces to execute in system hashrate benchmark
-    bench: Option<u64>,
-
-    #[structopt(long)]
-    /// Flag indicating whether to run miner in light mode
-    light_mode: bool,
-
-    #[structopt(long)]
-    /// Flag indicating whether to run miner with Large Pages
-    large_pages: bool,
-
-    #[structopt(long)]
-    /// Flag indicating whether to run miner with secure access to JIT memory (if supported)
-    secure: bool,
-
-    #[structopt(short, long, default_value = "4")]
-    /// PoW miner number of threads to use
-    threads: usize,
-
-    #[structopt(long)]
-    /// Assign full L3 cache to CPU
-    cache_qos: bool,
-
-    #[structopt(short, long, default_value = "2")]
-    /// Polling rate to ask darkfid for mining jobs
-    polling_rate: u64,
-
-    #[structopt(long, default_value = "0")]
-    /// Stop mining at given height (0 mines forever)
-    stop_at_height: u32,
-
-    #[structopt(short, long, default_value = "testnet")]
-    /// Blockchain network to use
-    network: String,
-
-    #[structopt(short, long)]
-    /// Set log file to ouput into
-    log: Option<String>,
-
-    #[structopt(short, parse(from_occurrences))]
-    /// Increase verbosity (-vvv supported)
-    verbose: u8,
-
-    #[structopt(long)]
-    /// Print CPU information
-    cpuid: bool,
-
-    #[structopt(long)]
-    /// Perform oneshot hashrate boost ops
-    boost: bool,
-}
-
-#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
-#[structopt()]
-/// Defines a blockchain network configuration.
-/// Default values correspond to a local network.
-pub struct BlockchainNetwork {
-    #[structopt(short, long, default_value = "tcp://127.0.0.1:8240")]
-    /// darkfid JSON-RPC endpoint
-    endpoint: Url,
-
-    #[structopt(long, default_value = "")]
-    /// Wallet mining address to receive mining rewards
-    recipient: String,
-
-    #[structopt(long)]
-    /// Optional contract spend hook to use in the mining reward
-    spend_hook: Option<String>,
-
-    #[structopt(long)]
-    /// Optional contract user data to use in the mining reward.
-    /// This is not arbitrary data.
-    user_data: Option<String>,
-}
-
-/// Auxiliary function to parse minerd configuration file and extract
-/// requested blockchain network config.
-pub async fn parse_blockchain_config(
-    config: Option<String>,
-    network: &str,
-) -> Result<BlockchainNetwork> {
-    // Grab config path
-    let config_path = get_config_path(config, CONFIG_FILE)?;
-    debug!(target: "minerd", "Parsing configuration file: {config_path:?}");
-
-    // Parse TOML file contents
-    let contents = read_to_string(&config_path).await?;
-    let contents: toml::Value = match toml::from_str(&contents) {
-        Ok(v) => v,
-        Err(e) => {
-            error!(target: "minerd", "Failed parsing TOML config: {e}");
-            return Err(Error::ParseFailed("Failed parsing TOML config"))
-        }
-    };
-
-    // Grab requested network config
-    let Some(table) = contents.as_table() else { return Err(Error::ParseFailed("TOML not a map")) };
-    let Some(network_configs) = table.get("network_config") else {
-        return Err(Error::ParseFailed("TOML does not contain network configurations"))
-    };
-    let Some(network_configs) = network_configs.as_table() else {
-        return Err(Error::ParseFailed("`network_config` not a map"))
-    };
-    let Some(network_config) = network_configs.get(network) else {
-        return Err(Error::ParseFailed("TOML does not contain requested network configuration"))
-    };
-    let network_config = toml::to_string(&network_config).unwrap();
-    let network_config =
-        match BlockchainNetwork::from_iter_with_toml::<Vec<String>>(&network_config, vec![]) {
-            Ok(v) => v,
-            Err(e) => {
-                error!(target: "minerd", "Failed parsing requested network configuration: {e}");
-                return Err(Error::ParseFailed("Failed parsing requested network configuration"))
-            }
-        };
-    debug!(target: "minerd", "Parsed network configuration: {network_config:?}");
-
-    Ok(network_config)
-}
-
-async_daemonize!(realmain);
-async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
-    if args.cpuid {
-        let cpuinfo = CpuInfo::detect();
-        println!("{}", cpuinfo);
-        return Ok(())
-    }
-
-    if args.boost {
-        let mut rxmsr = RxMsr::new();
-        // TODO: Right now I think we just assume default "-1" threads.
-        //       It might be incorrect, and would require a bit of config
-        //       modification to ensure something that supports defining
-        //       an array of specific cpus/threads to use.
-        let threads = CpuThreads::new(args.threads, 1);
-        rxmsr.init(args.cache_qos, threads.threads(), true);
-        return Ok(())
-    }
-
-    // Run system hashrate benchmark if requested
-    if let Some(nonces) = args.bench {
-        return benchmark(!args.light_mode, args.large_pages, args.secure, args.threads, nonces)
-    }
-
-    info!(target: "minerd", "Starting DarkFi Mining Daemon...");
-
-    // Grab blockchain network configuration
-    let blockchain_config = match args.network.as_str() {
-        "localnet" => parse_blockchain_config(args.config, "localnet").await?,
-        "testnet" => parse_blockchain_config(args.config, "testnet").await?,
-        "mainnet" => parse_blockchain_config(args.config, "mainnet").await?,
-        _ => {
-            error!(target: "minerd", "Unsupported chain `{}`", args.network);
-            return Err(Error::UnsupportedChain)
-        }
-    };
-    debug!(target: "minerd", "Blockchain config: {blockchain_config:?}");
-
-    // Parse the network wallet configuration
-    if Address::from_str(&blockchain_config.recipient).is_err() {
-        return Err(Error::InvalidAddress)
-    }
-    let mut wallet_config = HashMap::from([(
-        String::from("recipient"),
-        JsonValue::String(blockchain_config.recipient),
-    )]);
-
-    if let Some(spend_hook) = &blockchain_config.spend_hook {
-        if FuncId::from_str(spend_hook).is_err() {
-            return Err(Error::ParseFailed("Invalid spend hook"))
-        }
-        wallet_config.insert(String::from("spend_hook"), JsonValue::String(spend_hook.to_string()));
-    }
-
-    if let Some(user_data_string) = &blockchain_config.user_data {
-        let bytes: [u8; 32] = match bs58::decode(&user_data_string).into_vec()?.try_into() {
-            Ok(b) => b,
-            Err(_) => return Err(Error::ParseFailed("Invalid user data")),
-        };
-        let user_data: Option<pallas::Base> = pallas::Base::from_repr(bytes).into();
-        if user_data.is_none() {
-            return Err(Error::ParseFailed("Invalid user data"))
-        }
-        wallet_config
-            .insert(String::from("user_data"), JsonValue::String(user_data_string.to_string()));
-    }
-
-    // Generate the daemon
-    let miner_config = MinerNodeConfig::new(
-        !args.light_mode,
-        args.large_pages,
-        args.secure,
-        args.threads,
-        args.polling_rate,
-        args.stop_at_height,
-        wallet_config,
-    );
-    let daemon = Minerd::init(miner_config, blockchain_config.endpoint, &ex).await;
-    daemon.start(&ex);
-
-    // Signal handling for graceful termination.
-    let (signals_handler, signals_task) = SignalHandler::new(ex)?;
-    signals_handler.wait_termination(signals_task).await?;
-    info!(target: "minerd", "Caught termination signal, cleaning up and exiting");
-
-    daemon.stop().await;
-
-    info!(target: "minerd", "Shut down successfully");
-    Ok(())
-}

+ 0 - 361
bin/minerd/src/rpc.rs

@@ -1,361 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::sync::Arc;
-
-use num_bigint::BigUint;
-use randomx::RandomXVM;
-use tracing::{debug, error, info};
-use url::Url;
-
-use darkfi::{
-    blockchain::{Header, HeaderHash},
-    rpc::{client::RpcClient, jsonrpc::JsonRequest, util::JsonValue},
-    system::{sleep, ExecutorPtr, StoppableTask},
-    util::encoding::base64,
-    validator::pow::{generate_mining_vms, get_mining_flags, mine_block},
-    Error, Result,
-};
-use darkfi_serial::deserialize_async;
-
-use crate::{MinerNode, MinerNodePtr};
-
-/// Structure to hold a JSON-RPC client and its config,
-/// so we can recreate it in case of an error.
-pub struct DarkfidRpcClient {
-    endpoint: Url,
-    ex: ExecutorPtr,
-    client: Option<RpcClient>,
-}
-
-impl DarkfidRpcClient {
-    pub async fn new(endpoint: Url, ex: ExecutorPtr) -> Self {
-        let client = RpcClient::new(endpoint.clone(), ex.clone()).await.ok();
-        Self { endpoint, ex, client }
-    }
-
-    /// Stop the client.
-    pub async fn stop(&self) {
-        if let Some(ref client) = self.client {
-            client.stop().await
-        }
-    }
-}
-
-impl MinerNode {
-    /// Auxiliary function to request configured darkfid daemon for
-    /// its current mining RandomX key.
-    async fn randomx_key(&self) -> Result<HeaderHash> {
-        loop {
-            debug!(target: "minerd::rpc::randomx_key", "Executing mining RandomX key request to darkfid...");
-            let params = match self
-                .darkfid_daemon_request(
-                    "miner.get_current_mining_randomx_key",
-                    &JsonValue::Array(vec![]),
-                )
-                .await
-            {
-                Ok(params) => params,
-                Err(e) => {
-                    error!(target: "minerd::rpc::randomx_key", "darkfid request failed: {e}");
-                    self.sleep().await?;
-                    continue
-                }
-            };
-            debug!(target: "minerd::rpc::randomx_key", "Got reply: {params:?}");
-
-            // Verify response parameters
-            if !params.is_array() {
-                error!(target: "minerd::rpc::randomx_key", "darkfid responded with invalid params: {params:?}");
-                self.sleep().await?;
-                continue
-            }
-            let params = params.get::<Vec<JsonValue>>().unwrap();
-            if params.is_empty() {
-                debug!(target: "minerd::rpc::randomx_key", "darkfid response is empty");
-                self.sleep().await?;
-                continue
-            }
-            if params.len() != 1 || !params[0].is_string() {
-                error!(target: "minerd::rpc::randomx_key", "darkfid responded with invalid params: {params:?}");
-                self.sleep().await?;
-                continue
-            }
-
-            // Parse parameters
-            let Some(randomx_key_bytes) = base64::decode(params[0].get::<String>().unwrap()) else {
-                error!(target: "minerd::rpc::randomx_key", "Failed to parse RandomX key bytes");
-                self.sleep().await?;
-                continue
-            };
-            let Ok(randomx_key) = deserialize_async::<HeaderHash>(&randomx_key_bytes).await else {
-                error!(target: "minerd::rpc::randomx_key", "Failed to parse RandomX key");
-                self.sleep().await?;
-                continue
-            };
-
-            return Ok(randomx_key)
-        }
-    }
-
-    /// Auxiliary function to poll configured darkfid daemon for a new
-    /// mining job.
-    async fn poll(&self, header: &str) -> Result<(HeaderHash, BigUint, Header)> {
-        loop {
-            debug!(target: "minerd::rpc::poll", "Executing poll request to darkfid...");
-            let mut request_params = self.config.wallet_config.clone();
-            request_params.insert(String::from("header"), JsonValue::String(String::from(header)));
-            let params = match self
-                .darkfid_daemon_request("miner.get_header", &JsonValue::from(request_params))
-                .await
-            {
-                Ok(params) => params,
-                Err(e) => {
-                    error!(target: "minerd::rpc::poll", "darkfid poll failed: {e}");
-                    self.sleep().await?;
-                    continue
-                }
-            };
-            debug!(target: "minerd::rpc::poll", "Got reply: {params:?}");
-
-            // Verify response parameters
-            if !params.is_array() {
-                error!(target: "minerd::rpc::poll", "darkfid responded with invalid params: {params:?}");
-                self.sleep().await?;
-                continue
-            }
-            let params = params.get::<Vec<JsonValue>>().unwrap();
-            if params.is_empty() {
-                debug!(target: "minerd::rpc::poll", "darkfid response is empty");
-                self.sleep().await?;
-                continue
-            }
-            if params.len() != 3 ||
-                !params[0].is_string() ||
-                !params[1].is_string() ||
-                !params[2].is_string()
-            {
-                error!(target: "minerd::rpc::poll", "darkfid responded with invalid params: {params:?}");
-                self.sleep().await?;
-                continue
-            }
-
-            // Parse parameters
-            let Some(randomx_key_bytes) = base64::decode(params[0].get::<String>().unwrap()) else {
-                error!(target: "minerd::rpc::poll", "Failed to parse RandomX key bytes");
-                self.sleep().await?;
-                continue
-            };
-            let Ok(randomx_key) = deserialize_async::<HeaderHash>(&randomx_key_bytes).await else {
-                error!(target: "minerd::rpc::poll", "Failed to parse RandomX key");
-                self.sleep().await?;
-                continue
-            };
-            let Some(target_bytes) = base64::decode(params[1].get::<String>().unwrap()) else {
-                error!(target: "minerd::rpc::poll", "Failed to parse target bytes");
-                self.sleep().await?;
-                continue
-            };
-            let target = BigUint::from_bytes_le(&target_bytes);
-            let Some(header_bytes) = base64::decode(params[2].get::<String>().unwrap()) else {
-                error!(target: "minerd::rpc::poll", "Failed to parse header bytes");
-                self.sleep().await?;
-                continue
-            };
-            let Ok(header) = deserialize_async::<Header>(&header_bytes).await else {
-                error!(target: "minerd::rpc::poll", "Failed to parse header");
-                self.sleep().await?;
-                continue
-            };
-
-            return Ok((randomx_key, target, header))
-        }
-    }
-
-    /// Auxiliary function to submit a mining solution to configured
-    /// darkfid daemon.
-    async fn submit(&self, nonce: f64) -> String {
-        debug!(target: "minerd::rpc::submit", "Executing submit request to darkfid...");
-        let mut request_params = self.config.wallet_config.clone();
-        request_params.insert(String::from("nonce"), JsonValue::Number(nonce));
-        let result = match self
-            .darkfid_daemon_request("miner.submit_solution", &JsonValue::from(request_params))
-            .await
-        {
-            Ok(result) => result,
-            Err(e) => return format!("darkfid submit failed: {e}"),
-        };
-        debug!(target: "minerd::rpc::submit", "Got reply: {result:?}");
-
-        // Parse response
-        match result.get::<String>() {
-            Some(result) => result.clone(),
-            None => format!("darkfid responded with invalid params: {result:?}"),
-        }
-    }
-
-    /// Auxiliary function to execute a request towards the configured
-    /// darkfid daemon JSON-RPC endpoint.
-    async fn darkfid_daemon_request(&self, method: &str, params: &JsonValue) -> Result<JsonValue> {
-        let mut lock = self.rpc_client.write().await;
-        let req = JsonRequest::new(method, params.clone());
-
-        // Check the client is initialized
-        if let Some(ref client) = lock.client {
-            // Execute request
-            if let Ok(rep) = client.request(req.clone()).await {
-                drop(lock);
-                return Ok(rep);
-            }
-        }
-
-        // Reset the rpc client in case of an error and try again
-        let client = RpcClient::new(lock.endpoint.clone(), lock.ex.clone()).await?;
-        let rep = client.request(req).await?;
-        lock.client = Some(client);
-        drop(lock);
-        Ok(rep)
-    }
-
-    /// Auxiliary function to stop current JSON-RPC client, if its
-    /// initialized.
-    pub async fn stop_rpc_client(&self) {
-        self.rpc_client.read().await.stop().await;
-    }
-
-    /// Auxiliary function to sleep for configured polling rate time.
-    async fn sleep(&self) -> Result<()> {
-        // Check if stop signal is received
-        if self.mining_channel.1.is_full() {
-            debug!(target: "minerd::rpc::sleep", "Stop signal received, exiting polling task");
-            return Err(Error::DetachedTaskStopped);
-        }
-        debug!(target: "minerd::rpc::sleep", "Sleeping for {} until next poll...", self.config.polling_rate);
-        sleep(self.config.polling_rate).await;
-        Ok(())
-    }
-}
-
-/// Async task to poll darkfid for new mining jobs. Once a new job is
-/// received, spawns a mining task in the background.
-pub async fn polling_task(miner: MinerNodePtr, ex: ExecutorPtr) -> Result<()> {
-    // Cache current RandomX key and its VMs
-    let mut current_randomx_key = miner.randomx_key().await?;
-    info!(target: "minerd::rpc::mining_task", "Initializing {} mining VMs for key: {current_randomx_key}", miner.config.threads);
-    let mining_flags =
-        get_mining_flags(miner.config.fast_mode, miner.config.large_pages, miner.config.secure);
-    let mut _current_vms = Arc::new(generate_mining_vms(
-        mining_flags,
-        &current_randomx_key,
-        miner.config.threads,
-        &miner.mining_channel.1.clone(),
-    )?);
-
-    // Use the dummy Header on first poll
-    let mut current_job = current_randomx_key.to_string();
-    loop {
-        // Poll darkfid for a mining job
-        let (randomx_key, target, header) = miner.poll(&current_job).await?;
-        let header_hash = header.hash().to_string();
-        debug!(target: "minerd::rpc::polling_task", "Received job:");
-        debug!(target: "minerd::rpc::polling_task", "\tRandomX key - {randomx_key}");
-        debug!(target: "minerd::rpc::polling_task", "\tTarget - {target}");
-        debug!(target: "minerd::rpc::polling_task", "\tHeader - {header_hash}");
-
-        // Check if we are already processing this job
-        if header_hash == current_job {
-            debug!(target: "minerd::rpc::polling_task", "Already received job, skipping...");
-            miner.sleep().await?;
-            continue
-        }
-
-        // Check if we reached the stop height
-        if miner.config.stop_at_height > 0 && header.height > miner.config.stop_at_height {
-            info!(target: "minerd::rpc::polling_task", "Reached requested mining height: {}", miner.config.stop_at_height);
-            info!(target: "minerd::rpc::polling_task", "Daemon can be safely terminated now!");
-            break
-        }
-
-        info!(target: "minerd::rpc::polling_task", "Received new job to mine block header {header_hash} with key {randomx_key} for target: 0x{target:064x}");
-
-        // Abord pending mining job
-        miner.abort_mining().await;
-
-        // Check if the current RandomX key has changed
-        if randomx_key != current_randomx_key {
-            // Drop previous VMs
-            _current_vms = Arc::new(vec![]);
-
-            // Generate the RandomX VMs for the key
-            info!(target: "minerd::rpc::mining_task", "Initializing {} mining VMs for key: {randomx_key}", miner.config.threads);
-            _current_vms = Arc::new(generate_mining_vms(
-                mining_flags,
-                &randomx_key,
-                miner.config.threads,
-                &miner.mining_channel.1.clone(),
-            )?);
-            current_randomx_key = randomx_key;
-        }
-
-        // Detach mining task
-        StoppableTask::new().start(
-            mining_task(miner.clone(), _current_vms.clone(), target, header),
-            |res| async {
-                match res {
-                    Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
-                    Err(e) => error!(target: "minerd::rpc::polling_task", "Failed starting mining task: {e}"),
-                }
-            },
-            Error::DetachedTaskStopped,
-            ex.clone(),
-        );
-
-        // Update current job
-        current_job = header_hash;
-
-        // Sleep until next poll
-        miner.sleep().await?;
-    }
-
-    Ok(())
-}
-
-/// Async task to mine provided header and submit solution to darkfid.
-async fn mining_task(
-    miner: MinerNodePtr,
-    vms: Arc<Vec<Arc<RandomXVM>>>,
-    target: BigUint,
-    mut header: Header,
-) -> Result<()> {
-    // Mine provided block header
-    let header_hash = header.hash().to_string();
-    info!(target: "minerd::rpc::mining_task", "Mining block header {header_hash} for target: 0x{target:064x}");
-    if let Err(e) = mine_block(&vms, &target, &mut header, &miner.mining_channel.1.clone()) {
-        error!(target: "minerd::rpc::mining_task", "Failed mining block header {header_hash} with error: {e}");
-        return Err(Error::DetachedTaskStopped)
-    }
-    info!(target: "minerd::rpc::mining_task", "Mined block header {header_hash} with nonce: {}", header.nonce);
-    info!(target: "minerd::rpc::mining_task", "Mined block header hash: {}", header.hash());
-
-    // Submit solution to darkfid
-    info!(target: "minerd::rpc::submit", "Submitting solution to darkfid...");
-    let result = miner.submit(header.nonce as f64).await;
-    info!(target: "minerd::rpc::submit", "Submition result: {result}");
-
-    Ok(())
-}