03: engine comparability

This commit is contained in:
rob thijssen
2025-09-07 21:34:44 +08:00
parent 85a4818c09
commit ea40fdf6e5
8 changed files with 955 additions and 24 deletions

View File

@@ -1,8 +1,8 @@
# External Miner Service for Quantus Network
Note: This repository is now a Cargo workspace. Build and run the CLI with:
Note: This repository is now a Cargo workspace. Build and run the CLI with (the --num-cores flag remains available as an alias for --cores):
- cargo build -p miner-cli --release
- cargo run -p miner-cli -- --port 9833 [--metrics-port 9900] [--num-cores N]
- cargo run -p miner-cli -- --port 9833 [--metrics-port 9900] [--cores N]
This crate provides an external mining service that can be used with a Quantus Network node. It exposes an HTTP API for
managing mining jobs.
@@ -25,7 +25,7 @@ The service can be configured using command-line arguments or environment variab
| Argument | Environment Variable | Description | Default |
|-------------------|----------------------|--------------------------------------------|---------------|
| `--port <PORT>` | `MINER_PORT` | The port for the HTTP server to listen on. | `9833` |
| `--num-cores <N>` | `MINER_CORES` | The number of CPU cores to use for mining. | All available |
| `--cores <N>` (alias: `--num-cores`) | `MINER_CORES` | The number of CPU cores to use for mining. | All available |
Example:
@@ -34,7 +34,7 @@ Example:
../target/release/quantus-miner
# Run on a custom port with 4 cores
../target/release/quantus-miner --port 8000 --num-cores 4
../target/release/quantus-miner --port 8000 --cores 4
# Equivalent using environment variables
export MINER_PORT=8000
@@ -51,10 +51,10 @@ After building the service, you can run it directly from the command line:
RUST_LOG=info ../target/release/quantus-miner
# Run with a specific port and 2 cores
RUST_LOG=info ../target/release/quantus-miner --port 12345 --num-cores 2
RUST_LOG=info ../target/release/quantus-miner --port 12345 --cores 2
# Run in debug mode
RUST_LOG=info,miner=debug ../target/release/quantus-miner --num-cores 4
RUST_LOG=info,miner=debug ../target/release/quantus-miner --cores 4
```

View File

@@ -43,10 +43,19 @@ pub struct Candidate {
/// `Running` is included for potential future async/streaming engines.
#[derive(Clone, Debug)]
pub enum EngineStatus {
Running { hash_count: u64 },
Found(Candidate),
Exhausted { hash_count: u64 },
Cancelled { hash_count: u64 },
Running {
hash_count: u64,
},
Found {
candidate: Candidate,
hash_count: u64,
},
Exhausted {
hash_count: u64,
},
Cancelled {
hash_count: u64,
},
}
/// Abstract mining engine interface.
@@ -120,7 +129,10 @@ impl MinerEngine for BaselineCpuEngine {
work,
distance,
};
return EngineStatus::Found(candidate);
return EngineStatus::Found {
candidate,
hash_count,
};
}
// Advance or finish
@@ -182,11 +194,14 @@ impl MinerEngine for FastCpuEngine {
if is_valid_distance(ctx, distance) {
let work = current.to_big_endian();
return EngineStatus::Found(Candidate {
nonce: current,
work,
distance,
});
return EngineStatus::Found {
candidate: Candidate {
nonce: current,
work,
distance,
},
hash_count,
};
}
if current == range.end {

View File

@@ -11,7 +11,7 @@ struct Args {
port: u16,
/// Number of CPU cores to use for mining (defaults to all logical CPUs)
#[arg(long, env = "MINER_CORES")]
#[arg(long = "cores", alias = "num-cores", env = "MINER_CORES")]
num_cores: Option<usize>,
/// Optional Prometheus metrics exporter port; if omitted, metrics are disabled
@@ -48,7 +48,11 @@ impl From<EngineCli> for EngineSelection {
async fn main() {
let args = Args::parse();
// Initialize logger early to capture startup messages
// Initialize logger early to capture startup messages.
// If RUST_LOG is not set, default to info level for our app.
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info");
}
env_logger::init();
// Log effective configuration

View File

@@ -479,17 +479,20 @@ fn mine_range_with_engine(
};
match status {
engine_cpu::EngineStatus::Found(EngineCandidate {
nonce,
work,
distance,
}) => {
engine_cpu::EngineStatus::Found {
candidate: EngineCandidate {
nonce,
work,
distance,
},
hash_count,
} => {
final_result.result = Some(MiningJobResult {
nonce,
work,
distance,
});
final_result.hash_count = 0; // engine may not report per-iteration counts
final_result.hash_count = hash_count;
}
engine_cpu::EngineStatus::Exhausted { hash_count } => {
final_result.hash_count = hash_count;

View File

@@ -0,0 +1,405 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"type": "stat",
"title": "Engine Hash Rate Ratio (left/right)",
"description": "Ratio of total per-job hash rate between two selected engines",
"gridPos": { "h": 5, "w": 8, "x": 0, "y": 0 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum(miner_job_hash_rate{engine=~\"$engine_left\"}) / sum(miner_job_hash_rate{engine=~\"$engine_right\"})"
}
],
"options": {
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"orientation": "auto",
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"textMode": "value"
},
"fieldConfig": {
"defaults": { "unit": "none", "decimals": 2 },
"overrides": []
}
},
{
"type": "stat",
"title": "Solutions Found (24h) by Engine (left/right)",
"description": "Completed jobs increase in the last 24h for left and right engines",
"gridPos": { "h": 5, "w": 8, "x": 8, "y": 0 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "Left",
"expr": "sum(increase(miner_jobs_by_engine_total{engine=~\"$engine_left\", status=\"completed\"}[24h]))"
},
{
"refId": "Right",
"expr": "sum(increase(miner_jobs_by_engine_total{engine=~\"$engine_right\", status=\"completed\"}[24h]))"
}
],
"options": {
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"orientation": "horizontal",
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"textMode": "auto"
},
"fieldConfig": {
"defaults": { "unit": "none", "decimals": 0 },
"overrides": []
}
},
{
"type": "stat",
"title": "Global Hash Rate (All Engines)",
"description": "Sum of reported job hash rates across all engines",
"gridPos": { "h": 5, "w": 8, "x": 16, "y": 0 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum(miner_job_hash_rate)"
}
],
"options": {
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"orientation": "auto",
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"textMode": "value"
},
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
}
},
{
"type": "timeseries",
"title": "Hash Rate by Engine",
"description": "sum(miner_job_hash_rate) grouped by engine",
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 5 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum by (engine) (miner_job_hash_rate{engine=~\"$engine\"})",
"legendFormat": "{{engine}}"
}
],
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
},
"options": {
"legend": { "displayMode": "list", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "none" }
}
},
{
"type": "bargauge",
"title": "Jobs by Engine and Status (Total)",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 13 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum by (engine, status) (miner_jobs_by_engine_total{engine=~\"$engine\"})"
}
],
"fieldConfig": {
"defaults": { "unit": "none", "decimals": 0 },
"overrides": []
},
"options": {
"displayMode": "gradient",
"orientation": "horizontal",
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"showUnfilled": true
}
},
{
"type": "timeseries",
"title": "Per-Engine Total Hashes Increase (over time)",
"description": "increase(miner_job_hashes_total[$__rate_interval]) grouped by engine",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 13 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum by (engine) (increase(miner_job_hashes_total{engine=~\"$engine\"}[$__rate_interval]))",
"legendFormat": "{{engine}}"
}
],
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
},
"options": {
"legend": { "displayMode": "list", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "none" }
}
},
{
"type": "timeseries",
"title": "Average Per-Thread Hash Rate by Engine",
"description": "avg(miner_thread_hash_rate) grouped by engine (EMA-smoothed)",
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 21 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "avg by (engine) (miner_thread_hash_rate{engine=~\"$engine\"})",
"legendFormat": "{{engine}}"
}
],
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
},
"options": {
"legend": { "displayMode": "list", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "none" }
}
},
{
"type": "table",
"title": "Top 10 Jobs by Hash Rate (filter by engine)",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 29 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "topk(10, miner_job_hash_rate{engine=~\"$engine\"})"
}
],
"fieldConfig": {
"defaults": {
"custom": { "align": "auto" },
"unit": "ops",
"decimals": 2
},
"overrides": []
},
"options": {
"showHeader": true,
"footer": { "show": false },
"frameIndex": 0
}
},
{
"type": "table",
"title": "Per-Engine Total Hashes (5m increase)",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 29 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum by (engine) (increase(miner_job_hashes_total{engine=~\"$engine\"}[5m]))"
}
],
"fieldConfig": {
"defaults": {
"custom": { "align": "auto" },
"unit": "ops",
"decimals": 0
},
"overrides": []
},
"options": {
"showHeader": true,
"footer": { "show": false },
"frameIndex": 0
}
},
{
"type": "timeseries",
"title": "Per-Job Hash Rate by Engine (focus comparisons)",
"description": "Use filters above to narrow by engine(s) and job(s)",
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 37 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "miner_job_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}",
"legendFormat": "{{engine}} | {{job_id}}"
}
],
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
},
"options": {
"legend": { "displayMode": "list", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "none" }
}
},
{
"type": "table",
"title": "Per-Thread Hash Rate (filter by engine and job)",
"description": "EMA-smoothed per-thread rates with labels for engine/job/thread",
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 45 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\", thread_id=~\"$thread_id\"}"
}
],
"fieldConfig": {
"defaults": {
"custom": { "align": "auto" },
"unit": "ops",
"decimals": 2
},
"overrides": []
},
"options": {
"showHeader": true,
"footer": { "show": false },
"frameIndex": 0
}
}
],
"refresh": "10s",
"schemaVersion": 38,
"style": "dark",
"tags": ["quantus", "miner", "prometheus", "engines", "comparison"],
"templating": {
"list": [
{
"type": "datasource",
"name": "DS_PROMETHEUS",
"hide": 0,
"query": "prometheus",
"current": {},
"options": []
},
{
"type": "query",
"name": "engine",
"label": "Engine(s)",
"hide": 0,
"datasource": "$DS_PROMETHEUS",
"definition": "label_values(miner_job_hash_rate, engine)",
"includeAll": true,
"multi": true,
"refresh": 2,
"sort": 1,
"query": "label_values(miner_job_hash_rate, engine)",
"regex": "",
"current": {
"selected": false,
"text": ["cpu-baseline", "cpu-fast"],
"value": ["cpu-baseline", "cpu-fast"]
}
},
{
"type": "query",
"name": "engine_left",
"label": "Left Engine",
"hide": 0,
"datasource": "$DS_PROMETHEUS",
"definition": "label_values(miner_job_hash_rate, engine)",
"includeAll": false,
"multi": false,
"refresh": 2,
"sort": 1,
"query": "label_values(miner_job_hash_rate, engine)",
"regex": "",
"current": {
"selected": true,
"text": "cpu-fast",
"value": "cpu-fast"
}
},
{
"type": "query",
"name": "engine_right",
"label": "Right Engine",
"hide": 0,
"datasource": "$DS_PROMETHEUS",
"definition": "label_values(miner_job_hash_rate, engine)",
"includeAll": false,
"multi": false,
"refresh": 2,
"sort": 1,
"query": "label_values(miner_job_hash_rate, engine)",
"regex": "",
"current": {
"selected": true,
"text": "cpu-baseline",
"value": "cpu-baseline"
}
},
{
"type": "query",
"name": "job_id",
"label": "Job ID",
"hide": 0,
"datasource": "$DS_PROMETHEUS",
"definition": "label_values(miner_job_hash_rate{engine=~\"$engine\"}, job_id)",
"includeAll": true,
"multi": true,
"refresh": 2,
"sort": 1,
"query": "label_values(miner_job_hash_rate{engine=~\"$engine\"}, job_id)",
"regex": "",
"current": {
"selected": false,
"text": ["All"],
"value": ["$__all"]
}
},
{
"type": "query",
"name": "thread_id",
"label": "Thread ID",
"hide": 0,
"datasource": "$DS_PROMETHEUS",
"definition": "label_values(miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}, thread_id)",
"includeAll": true,
"multi": true,
"refresh": 2,
"sort": 1,
"query": "label_values(miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}, thread_id)",
"regex": "",
"current": {
"selected": false,
"text": ["All"],
"value": ["$__all"]
}
}
]
},
"time": { "from": "now-6h", "to": "now" },
"timepicker": {},
"timezone": "",
"title": "Quantus Miner - Engines Comparison",
"version": 1,
"uid": null
}

View File

@@ -0,0 +1,302 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"type": "stat",
"title": "Global Hash Rate (nonces/sec)",
"description": "Sum of reported miner hash rate across all jobs and engines",
"gridPos": { "h": 5, "w": 8, "x": 0, "y": 0 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum(miner_hash_rate)"
}
],
"options": {
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"orientation": "auto",
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"textMode": "value"
},
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
}
},
{
"type": "timeseries",
"title": "Global Hash Rate (nonces/sec) over time",
"gridPos": { "h": 8, "w": 16, "x": 8, "y": 0 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum(miner_hash_rate)",
"legendFormat": "global"
}
],
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
},
"options": {
"legend": { "displayMode": "list", "placement": "bottom", "calcs": [] },
"tooltip": { "mode": "multi", "sort": "none" }
}
},
{
"type": "bargauge",
"title": "Jobs by Status (total)",
"gridPos": { "h": 8, "w": 8, "x": 0, "y": 5 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum by (status) (miner_jobs_total)"
}
],
"fieldConfig": {
"defaults": { "unit": "none", "decimals": 0 },
"overrides": []
},
"options": {
"displayMode": "gradient",
"orientation": "horizontal",
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"showUnfilled": true
}
},
{
"type": "bargauge",
"title": "Jobs by Engine and Status (total)",
"gridPos": { "h": 8, "w": 8, "x": 0, "y": 13 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum by (engine, status) (miner_jobs_by_engine_total)"
}
],
"fieldConfig": {
"defaults": { "unit": "none", "decimals": 0 },
"overrides": []
},
"options": {
"displayMode": "gradient",
"orientation": "horizontal",
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"showUnfilled": true
}
},
{
"type": "timeseries",
"title": "Job Hash Rate by Job (filter by engine)",
"gridPos": { "h": 8, "w": 16, "x": 8, "y": 8 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum by (job_id) (miner_job_hash_rate{engine=~\"$engine\"})",
"legendFormat": "{{job_id}}"
}
],
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
},
"options": {
"legend": { "displayMode": "list", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "none" }
}
},
{
"type": "timeseries",
"title": "Thread Hash Rate (filter by engine and job_id)",
"description": "EMA-smoothed per-thread rates. Use variables above to filter.",
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 21 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\", thread_id=~\"$thread_id\"}",
"legendFormat": "{{engine}} | job={{job_id}} | th={{thread_id}}"
}
],
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
},
"options": {
"legend": { "displayMode": "list", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "none" }
}
},
{
"type": "stat",
"title": "Total Hashes (5m increase)",
"gridPos": { "h": 5, "w": 8, "x": 0, "y": 29 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "increase(miner_hashes_total[5m])"
}
],
"options": {
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"orientation": "auto",
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"textMode": "value"
},
"fieldConfig": {
"defaults": { "unit": "ops" },
"overrides": []
}
},
{
"type": "table",
"title": "Per-Job Hashes (5m increase, filter by engine)",
"gridPos": { "h": 8, "w": 16, "x": 8, "y": 29 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "sum by (engine, job_id) (increase(miner_job_hashes_total{engine=~\"$engine\"}[5m]))"
}
],
"fieldConfig": {
"defaults": { "custom": { "align": "auto" } },
"overrides": []
},
"options": {
"showHeader": true,
"footer": { "show": false },
"frameIndex": 0
}
},
{
"type": "timeseries",
"title": "Job Status Gauges (running/completed/failed/cancelled)",
"description": "Job status gauges as time series (1 for current status, else 0). Filter by engine/job.",
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 37 },
"datasource": "$DS_PROMETHEUS",
"targets": [
{
"refId": "A",
"expr": "miner_job_status{engine=~\"$engine\", job_id=~\"$job_id\"}",
"legendFormat": "{{job_id}}:{{status}}"
}
],
"fieldConfig": {
"defaults": { "unit": "none", "min": 0, "max": 1 },
"overrides": []
},
"options": {
"legend": { "displayMode": "list", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "none" }
}
}
],
"refresh": "10s",
"schemaVersion": 38,
"style": "dark",
"tags": ["quantus", "miner", "prometheus"],
"templating": {
"list": [
{
"type": "datasource",
"name": "DS_PROMETHEUS",
"hide": 0,
"query": "prometheus",
"current": {},
"options": []
},
{
"type": "query",
"name": "engine",
"label": "Engine",
"hide": 0,
"datasource": "$DS_PROMETHEUS",
"definition": "label_values(miner_job_hash_rate, engine)",
"includeAll": true,
"multi": true,
"refresh": 2,
"sort": 1,
"query": "label_values(miner_job_hash_rate, engine)",
"regex": "",
"current": {
"selected": false,
"text": ["All"],
"value": ["$__all"]
}
},
{
"type": "query",
"name": "job_id",
"label": "Job ID",
"hide": 0,
"datasource": "$DS_PROMETHEUS",
"definition": "label_values(miner_job_hash_rate{engine=~\"$engine\"}, job_id)",
"includeAll": true,
"multi": true,
"refresh": 2,
"sort": 1,
"query": "label_values(miner_job_hash_rate{engine=~\"$engine\"}, job_id)",
"regex": "",
"current": {
"selected": false,
"text": ["All"],
"value": ["$__all"]
}
},
{
"type": "query",
"name": "thread_id",
"label": "Thread ID",
"hide": 0,
"datasource": "$DS_PROMETHEUS",
"definition": "label_values(miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}, thread_id)",
"includeAll": true,
"multi": true,
"refresh": 2,
"sort": 1,
"query": "label_values(miner_thread_hash_rate{engine=~\"$engine\", job_id=~\"$job_id\"}, thread_id)",
"regex": "",
"current": {
"selected": false,
"text": ["All"],
"value": ["$__all"]
}
}
]
},
"time": { "from": "now-6h", "to": "now" },
"timepicker": {},
"timezone": "",
"title": "Quantus Miner - Overview",
"version": 1,
"uid": null
}

View File

@@ -0,0 +1,113 @@
# Prompt Summary (03): Accurate Metrics for Engine Comparisons, Logging Defaults, CLI Flag Rename, and Dashboards
## Context
To compare the performance of different mining engines (e.g., `cpu-baseline` vs `cpu-fast`) in Grafana using Prometheus metrics, we need precise per-job and per-thread hash counts. Currently, when a worker thread finds a solution, the engine reports only the `Candidate` without the associated `hash_count`. This causes undercounting in totals/rates, especially for the winning thread.
Additionally, we want a better out-of-the-box operator experience and evaluation tooling:
- Default the logging level to `info` when `RUST_LOG` is not set.
- Rename the CLI flag `--num-cores` to `--cores` but keep the internal field `num_cores`, and preserve `--num-cores` as an alias for backward compatibility.
- Provide two Grafana dashboards:
- A “Miner Overview” dashboard for operators, focusing on the fastest implementation.
- An “Engines Comparison” dashboard for performance evaluations across implementations.
We want to extend the engines result reporting so that the final “Found” event carries the `hash_count` accumulated by that thread. The miner-service will consume this information to update metrics correctly (global, per-job, per-thread), enabling accurate comparison between engines.
We also want sane defaults for logging, a consistent CLI, and packaged dashboards to support evaluation and production usage.
## Objectives
- Extend the engine result type to carry per-thread `hash_count` when a solution is found.
- Update both CPU engines (`cpu-baseline` and `cpu-fast`) to return the `hash_count` on `Found`.
- Update the miner-service to:
- Include `hash_count` from the `Found` status in totals and rate calculations.
- Maintain engine-aware, per-job, and per-thread metrics parity.
- Preserve the node-facing HTTP API and service behavior.
- Keep engine selection and metrics toggle behavior unchanged.
- Default RUST_LOG to `info` if not set, so operators get useful logs without extra env configuration.
- Rename CLI flag to `--cores` (keep `--num-cores` as an alias) while keeping the code field as `num_cores`.
- Add two Grafana dashboards under `docs/grafana/`:
- `miner-dashboard.json` for operators running the fastest engine.
- `engines-comparison-dashboard.json` for evaluating implementations.
## Requirements
- Engine API:
- Change `EngineStatus::Found(Candidate)` to carry `{ candidate: Candidate, hash_count: u64 }`.
- Maintain the existing `Exhausted { hash_count }` and `Cancelled { hash_count }` patterns for consistency.
- Engines:
- `BaselineCpuEngine` and `FastCpuEngine` must return accurate `hash_count` on `Found`.
- Service:
- In `update_from_results`, when a `ThreadResult` is built from an engines `Found`, carry the returned `hash_count` and accumulate it in:
- Global totals: `miner_hashes_total`
- Per-job totals: `miner_job_hashes_total{engine,job_id}`
- Per-thread totals: `miner_thread_hashes_total{engine,job_id,thread_id}`
- Ensure per-thread delta-rate and EMA smoothing logic still runs using the `hash_count` returned by `Found`.
- Metrics:
- No new metrics required; existing labeled counters/gauges should be used.
- Accuracy improves because the winning threads final work is now captured.
- Logging:
- If `RUST_LOG` is unset, default to `info` at startup.
- CLI:
- Rename `--num-cores` to `--cores`, keeping `--num-cores` as an alias and `num_cores` as the field name.
- Dashboards:
- Add `docs/grafana/miner-dashboard.json` (operator view).
- Add `docs/grafana/engines-comparison-dashboard.json` (evaluation view).
## Constraints and Compatibility
- The public HTTP API (endpoints/payloads) must remain unchanged.
- The engine abstraction is internal to the repo, so the type change is acceptable and should be updated wherever consumed (service only).
- Maintain compatibility between `cpu-baseline` and `cpu-fast` engines for apples-to-apples comparison.
- Logging default must not override an explicitly set `RUST_LOG`.
- CLI change must preserve backward compatibility (`--num-cores` alias).
- Dashboards must be optional artifacts (no runtime impact).
## Acceptance Criteria
- `EngineStatus::Found` carries `{ candidate, hash_count }`.
- Both engines report the correct `hash_count` when returning `Found`.
- Miner-service uses this `hash_count` to update:
- `miner_hashes_total`
- `miner_job_hashes_total{engine,job_id}`
- `miner_thread_hashes_total{engine,job_id,thread_id}`
- Per-thread delta-rate → `miner_thread_hash_rate{engine,job_id,thread_id}` (EMA-smoothed).
- Build success in release mode.
- No changes to node-facing HTTP behavior.
- Grafana can now accurately compare `cpu-baseline` vs `cpu-fast` with improved fidelity for the winning thread.
- When `RUST_LOG` is unset, logs default to `info`.
- `--cores` is available (with `--num-cores` as an alias) and maps to `num_cores`.
- Two dashboards exist under `docs/grafana/` for operators and engine evaluation.
## Deliverables
- Engine API update:
- Enum change for `EngineStatus::Found` to include `hash_count`.
- Engine implementations:
- Baseline and fast CPU engines updated to return `hash_count` on `Found`.
- Service updates:
- `mine_range_with_engine` and `update_from_results` modified to forward and accumulate the `hash_count` from `Found`.
- Metrics update hooks remain correct and comprehensive (global, per-job, per-thread, per-thread EMA rate).
- CLI/logging updates:
- Default `RUST_LOG` to `info` when unset.
- Expose `--cores` flag (keep `--num-cores` alias) while retaining `num_cores` as the code field.
- Dashboards:
- `docs/grafana/miner-dashboard.json` (operator-focused).
- `docs/grafana/engines-comparison-dashboard.json` (engine evaluation).
- Documentation:
- Capture this prompt and a separate response summary as iteration “03” in `docs/implementation/`.
## Out of Scope
- Additional metrics beyond those already implemented.
- Changes to the HTTP API or external protocol.
- Performance optimizations (e.g., Montgomery arithmetic) not directly related to the `hash_count` reporting tweak.
- Further CLI renames or logging customization (beyond defaults).
- Grafana provisioning/export automation.
## Notes
- This tweak corrects the main remaining accuracy gap for the winning thread in metrics.
- With this change, Grafana dashboards can make fair and accurate comparisons between different engines using the `engine` label, across global/job/thread-level telemetry.
- Default logging reduces operational friction; CLI alias retains backward compatibility during the transition.
- Packaged dashboards (overview and engines comparison) provide out-of-the-box visibility for operators and evaluators.

View File

@@ -0,0 +1,89 @@
# Response Summary (03): Engine `Found` hash_count Included for Accurate Metrics, Logging/CLI Updates, and Dashboards
This document summarizes the implementation that augments the mining engines result reporting so that the final “Found” event carries the per-thread `hash_count`. This enables accurate global, per-job, and per-thread metrics (including rates), improving Grafana comparisons between engines (e.g., `cpu-baseline` vs `cpu-fast`).
## Overview
- Extended the engine result type so `Found` includes `{ candidate, hash_count }`.
- Updated both CPU engines to return the final `hash_count` on `Found`.
- Modified the service to forward and accumulate this count:
- Global totals (`miner_hashes_total`)
- Per-job totals (`miner_job_hashes_total{engine,job_id}`)
- Per-thread totals (`miner_thread_hashes_total{engine,job_id,thread_id}`)
- Per-thread hash-rate gauge via delta-rate with EMA smoothing
- No changes to the node-facing HTTP API.
## Changes Implemented
### 1) Engine API
- File: `crates/engine-cpu/src/lib.rs`
- Updated `EngineStatus`:
- Before: `Found(Candidate)`
- Now: `Found { candidate: Candidate, hash_count: u64 }`
- Other variants (`Exhausted`, `Cancelled`, `Running`) retain their `hash_count` shape for consistency.
### 2) Engines
- Files: `crates/engine-cpu/src/lib.rs`
- `BaselineCpuEngine`:
- On discovering a solution, returns `EngineStatus::Found { candidate, hash_count }` with the accumulated count for the current range.
- `FastCpuEngine`:
- Same behavior as baseline but using the incremental path (init once with `init_worker_y0`, then `step_mul` per nonce).
### 3) Service
- File: `crates/miner-service/src/lib.rs`
- `mine_range_with_engine`:
- Updated to extract `hash_count` from `EngineStatus::Found` and include it in the `ThreadResult`.
- `update_from_results`:
- Accumulates `hash_count` into:
- `total_hash_count`
- Global/engine-aware per-job/per-thread counters
- Per-thread delta-rate calculation with EMA smoothing now uses the `hash_count` from `Found` as well.
- All metric updates are engine-aware and job-/thread-labeled where appropriate.
### 4) Metrics
- No new metrics introduced; existing labeled series are now fully accurate for winning threads:
- Global:
- `miner_jobs_total{status}`
- `miner_hashes_total`
- `miner_hash_rate`
- Engine-/job-aware:
- `miner_job_hashes_total{engine,job_id}`
- `miner_job_hash_rate{engine,job_id}`
- `miner_job_status{engine,job_id,status}` (IntGauge)
- `miner_jobs_by_engine_total{engine,status}`
- Engine-/job-/thread-aware:
- `miner_thread_hashes_total{engine,job_id,thread_id}`
- `miner_thread_hash_rate{engine,job_id,thread_id}` (EMA-smoothed)
## Compatibility
- The public HTTP API (endpoints and payloads) remains unchanged.
- Engine abstraction change is internal to the workspace; service updated accordingly.
- Works with both `cpu-baseline` and `cpu-fast`, enabling apples-to-apples comparisons using the `engine` label in Grafana.
## Build and Run
- Build (release):
- `cargo build -p miner-cli --release`
- Run (baseline, no metrics):
- `cargo run -p miner-cli -- --port 9833 --engine cpu-baseline`
- Run (fast engine, with metrics):
- `cargo run -p miner-cli -- --port 9833 --engine cpu-fast --metrics-port 9900`
- Scrape: `http://localhost:9900/metrics`
## Verification
- Compile-time: Verified in release mode after updating the service to consume the new `Found` shape.
- Runtime: The winning threads final work is now counted in totals and informs the per-thread rate EMA, reducing undercounting and improving rate fidelity.
## Notes and Next Steps
- With winning-thread `hash_count` included, Grafana dashboards can fairly compare engines across global/job/thread metrics.
- Next logical optimization steps:
- Add correctness tests that compare baseline vs fast engine outcomes on small ranges (golden tests).
- Add benchmark harness to quantify nonces/sec improvements.
- Implement Montgomery multiplication in `pow-core` and switch `cpu-fast` to it for further speedups.