Skip to content

main#11

Open
thegreatbey wants to merge 3 commits into
mrv777:mainfrom
thegreatbey:main
Open

main#11
thegreatbey wants to merge 3 commits into
mrv777:mainfrom
thegreatbey:main

Conversation

@thegreatbey
Copy link
Copy Markdown

@thegreatbey thegreatbey commented Apr 25, 2026

# Bitaxe Hashrate Benchmark

A Python-based benchmarking tool for optimizing Bitaxe mining performance by testing different voltage and frequency combinations while monitoring hashrate, temperature, and power efficiency.

## Features

- Automated benchmarking of different voltage/frequency combinations

- Temperature monitoring and safety cutoffs

- Power efficiency calculations (J/TH)

- Automatic saving of benchmark results

- Graceful shutdown with best settings retention

- Docker support for easy deployment

## Prerequisites

- Python 3.11 or higher

- Access to a Bitaxe miner on your network

- Docker (optional, for containerized deployment)

- Git (optional, for cloning the repository)

## Installation

### Standard Installation

1. Clone the repository:

git clone https://github.com/mrv777/Bitaxe-Hashrate-Benchmark.git

cd Bitaxe-Hashrate-Benchmark

2. Create and activate a virtual environment:

python -m venv venv

\# On Windows

venv\\Scripts\\activate

\# On Linux/Mac

source venv/bin/activate

3. Install dependencies:

pip install -r requirements.txt

### Docker Installation

1. Build the Docker image:

docker build -t bitaxe-benchmark .

## Usage

### Standard Usage

Run the benchmark tool by providing your Bitaxe's IP address:

python bitaxe\_hashrate\_benchmark.py <bitaxe\_ip>

Optional parameters:

- -v, --voltage: Initial voltage in mV (default: 1150)

- -f, --frequency: Initial frequency in MHz (default: 500)

Example:

python bitaxe\_hashrate\_benchmark.py 192.168.2.29 -v 1175 -f 775

### Docker Usage (Optional)

Run the container with your Bitaxe's IP address:

docker run --rm bitaxe-benchmark <bitaxe\_ip> \[options]

Example:

docker run --rm bitaxe-benchmark 192.168.2.26 -v 1200 -f 550

### Quick Test (Mock, Fast)

Use the built-in mock to verify end-to-end flow without hardware.

1) Start the mock API (new shell):

python -m venv venv

venv\\Scripts\\activate

pip install flask

python mock\_bitaxe.py

2) In another shell, run a short smoke test (≥7 samples but fast):

venv\\Scripts\\activate

python bitaxe\_hashrate\_benchmark.py 127.0.0.1:5000 --quick

Notes:

- --quick sets a short stabilization and sampling window for fast validation.

- Results are saved to a timestamped JSON file in the repo directory.

## Configuration

The script includes several configurable parameters:

- Maximum chip temperature: 66°C

- Maximum VR temperature: 86°C

- Maximum allowed voltage: 1400mV

- Minimum allowed voltage: 1000mV

- Maximum allowed frequency: 1200MHz

- Maximum power consumption: 40W

- Minimum allowed frequency: 400MHz

- Minimum input voltage: 4800mV

- Maximum input voltage: 5500mV

- Benchmark duration: 10 minutes

- Sample interval: 15 seconds

- Sleep time before benchmark: 90 seconds

- **Minimum required samples: 7** (for valid data processing)

- Voltage increment: 20mV

- Frequency increment: 25MHz

## Output

The benchmark results are saved to bitaxe\_benchmark\_results\_<ip\_address>.json, containing:

- Complete test results for all combinations

- Top 5 performing configurations ranked by hashrate

- Top 5 most efficient configurations ranked by J/TH

- For each configuration:

- Average hashrate (with outlier removal)

- Temperature readings (excluding initial warmup period)

- VR temperature readings (when available)

- Power efficiency metrics (J/TH)

- Input voltage measurements

- Voltage/frequency combinations tested

## Safety Features

- Automatic temperature monitoring with safety cutoff (66°C chip temp)

- Voltage regulator (VR) temperature monitoring with safety cutoff (86°C)

- Input voltage monitoring with minimum threshold (4800mV) and maximum threshold (5500mV)

- Power consumption monitoring with safety cutoff (40W)

- Temperature validation (must be above 5°C)

- Graceful shutdown on interruption (Ctrl+C)

- Automatic reset to best performing settings after benchmarking

- Input validation for safe voltage and frequency ranges

- Hashrate validation to ensure stability

- Protection against invalid system data

- Outlier removal from benchmark results

## Benchmarking Process

The tool follows this process:

1. Starts with user-specified or default voltage/frequency

2. Tests each combination for 10 minutes

3. Validates hashrate is within 6% of theoretical maximum

4. Incrementally adjusts settings:

- Increases frequency if stable

- Increases voltage if unstable

- Stops at thermal or stability limits

5. Records and ranks all successful configurations

6. Automatically applies the best performing stable settings

7. Restarts system after each test for stability

8. Allows 90-second stabilization period between tests

## Data Processing

The tool implements several data processing techniques to ensure accurate results:

- Removes 3 highest and 3 lowest hashrate readings to eliminate outliers

- Excludes first 6 temperature readings during warmup period

- Validates hashrate is within 6% of theoretical maximum

- Averages power consumption across entire test period

- Monitors VR temperature when available

- Calculates efficiency in Joules per Terahash (J/TH)

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details.

## Disclaimer

Please use this tool responsibly. Overclocking and voltage modifications can potentially damage your hardware if not done carefully. Always ensure proper cooling and monitor your device during benchmarking.

## Safe Mock Mode

Run the benchmark against a local mock server so the script “succeeds” without touching a real Bitaxe.

1) Start the mock on your host:

\# Windows (PowerShell)

python -m venv venv

venv\\Scripts\\activate

pip install flask

python mock\_bitaxe.py



\# macOS/Linux

\# python -m venv venv

\# source venv/bin/activate

\# pip install flask

\# python mock\_bitaxe.py

2) Build and run the container, pointing at your host mock:

docker build -t bitaxe-benchmark .

docker run --rm bitaxe-benchmark host.docker.internal:5000

Notes:

- The mock implements the endpoints the script calls: GET /api/system/info, GET /api/system/asic, PATCH /api/system, POST /api/system/restart.

- Host host.docker.internal is commonly available on Docker Desktop to reach the host from a container.

- This keeps real hardware safe while allowing full script flow.

Summary by CodeRabbit

  • New Features

    • Added --quick mode for rapid smoke testing with shortened runtime.
    • Added CLI options for tunable benchmark timing (--sleep-time, --benchmark-time, --sample-interval).
    • Introduced mock API server for testing without hardware.
  • Documentation

    • Enhanced README with quick test and safe mock mode workflows.
    • Updated benchmark testing parameters and validation thresholds.
  • Improvements

    • Improved filename sanitization for filesystem compatibility.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

📝 Walkthrough

Walkthrough

The pull request introduces a new mock BitAxe Flask server (mock_bitaxe.py) with in-memory device state and multiple API endpoints, enhances the benchmark script with CLI-tunable timing options and a --quick mode, documents mock-based testing workflows in the README, and updates gitignore rules for benchmark artifacts.

Changes

Cohort / File(s) Summary
Configuration & Documentation
.gitignore, README.md
Added gitignore entries for benchmark result JSON files and legacy mock module. README expanded with two mock-based workflows ("Quick Test" and "Safe Mock Mode"), updated benchmark timing (10 min vs 20 min) and tolerance thresholds (6% vs 8%), documented mock API endpoints, and corrected EOF newline.
Benchmark Script Enhancement
bitaxe_hashrate_benchmark.py
Added CLI options for --sleep-time, --benchmark-time, --sample-interval, and --quick mode. Implemented IP address sanitization in JSON filename generation and added absolute path output to save results and completion messages.
New Mock Server
mock_bitaxe.py
New Flask application with in-memory device state management. Implements five HTTP endpoints: GET /api/system/info (returns system configuration with computed hashRate), GET /api/system/asic (returns voltage/frequency defaults), PATCH /api/system (updates core voltage and frequency), POST /api/system/restart (no-op status return), and GET /api/hashrate (legacy zero-filled response). Includes helper functions for expected and mock hashrate computation.

Sequence Diagram(s)

sequenceDiagram
    participant BS as Benchmark Script
    participant MS as Mock Server
    participant State as Device State

    BS->>MS: GET /api/system/info
    MS->>State: Read current config
    State-->>MS: frequency, voltage, core count
    MS->>MS: Compute hashRate
    MS-->>BS: Return system info + hashRate

    BS->>MS: GET /api/system/asic
    MS->>State: Read defaults
    State-->>MS: Default voltage/frequency
    MS-->>BS: Return ASIC config

    BS->>MS: PATCH /api/system (new frequency/voltage)
    MS->>State: Update settings
    State-->>MS: Confirm update
    MS-->>BS: Return updated values

    BS->>MS: GET /api/system/info
    MS->>State: Read updated config
    State-->>MS: New frequency, recalculated hashRate
    MS-->>BS: Return updated info

    BS->>MS: POST /api/system/restart
    MS-->>BS: Return restart status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Update bitaxe_hashrate_benchmark.py #1: Modifies result filename generation in bitaxe_hashrate_benchmark.py—this PR sanitizes IP addresses for filesystem compatibility while the related PR adds timestamp prefixes, both affecting the same filename output mechanism.

Poem

🐰 A mock server hops into the ring,
With frequencies and voltages to bring,
Endpoints quick, and benchmarks fast,
Testing dreams that finally last!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'main' is vague and generic, providing no meaningful information about the changes made. It does not convey what was added or modified in this pull request. Use a descriptive title that summarizes the primary change, such as 'Add mock Bitaxe benchmarking server and CLI timing options' or 'Introduce mock mode and configurability for benchmark testing'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (7)
.gitignore (2)

174-175: Project-specific “OLD” file ignore is a smell.

Tracking mock_bitaxe_OLD.py in the shared .gitignore suggests the file is being kept around locally. Prefer either deleting it (Git history retains old versions) or adding it to your personal .git/info/exclude rather than committing the rule for everyone.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore around lines 174 - 175, The .gitignore currently contains a
project-wide rule ignoring "mock_bitaxe_OLD.py" which is a local,
developer-specific artifact; remove the "mock_bitaxe_OLD.py" entry from the
shared .gitignore and either delete the file (history preserves it) or add the
filename to your personal exclude file (.git/info/exclude) instead so the ignore
stays local; update the repository by committing the .gitignore removal and, if
you keep the file locally, add it to your .git/info/exclude on your machine.

41-41: Duplicate and misplaced ignore pattern.

*benchmark_results*.json is added here under the "Unit test / coverage reports" section, and again at line 172 under the more appropriate "Benchmark results" section. Drop the entry on line 41 to keep this section semantically clean.

♻️ Proposed fix
 # Unit test / coverage reports
 htmlcov/
-*benchmark_results*.json
 .tox/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore at line 41, Remove the duplicate ignore pattern
"*benchmark_results*.json" from the "Unit test / coverage reports" section in
.gitignore so it only exists under the correct "Benchmark results" section;
locate the occurrence of "*benchmark_results*.json" near the top of the file (in
the unit test/coverage block) and delete that entry, keeping the existing
pattern in the benchmark section intact.
bitaxe_hashrate_benchmark.py (3)

98-100: Capping max_allowed_frequency is safer than it looks — but document the side-effect.

Setting max_allowed_frequency = initial_frequency ensures the main loop terminates after one iteration (line 453's current_frequency + frequency_increment <= max_allowed_frequency becomes false), but it also silently overrides the operator-facing safety constant for the entire run. If anyone later prints or logs max_allowed_frequency for diagnostics, they'll see a misleading value in --quick runs. Consider introducing a separate quick_iteration_limit flag or breaking after the first successful iteration based on args.quick, instead of mutating a safety limit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bitaxe_hashrate_benchmark.py` around lines 98 - 100, The current quick-mode
mutates the safety variable max_allowed_frequency by setting it to
initial_frequency when args.quick is true; instead, avoid changing
max_allowed_frequency and implement a separate mechanism such as a
quick_iteration_limit flag or break-after-first-success logic: introduce a new
boolean or counter (e.g., quick_iteration_limit or did_one_iteration) and,
inside the main loop that uses current_frequency and frequency_increment, check
args.quick and break after completing the first successful iteration (or
decrement the counter) rather than assigning initial_frequency to
max_allowed_frequency so diagnostic logs of max_allowed_frequency remain
correct.

529-538: Sanitization only handles : — fine for IPv4:port, but worth noting.

safe_ip = ip_address.replace(':', '_') is enough for 127.0.0.1:5000, but if a user ever passes an IPv6 literal (Bitaxe doesn't today, but the CLI accepts arbitrary text) the resulting filename will contain [ and ] which are valid on POSIX but cosmetically ugly. Not a blocker — flagging only because the helper-extraction comment above would centralize this rule for future hardening.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bitaxe_hashrate_benchmark.py` around lines 529 - 538, The current filename
sanitization only replaces ':' via safe_ip = ip_address.replace(':', '_'), which
misses characters like '[' and ']' (IPv6) and other potentially problematic
chars; update the sanitization to a reusable helper (e.g., create a
sanitize_filename or sanitize_ip helper used where safe_ip is set) that replaces
or strips characters not allowed/desired in filenames (at minimum replace ':'
'[' ']' and any path separators like '/' '\' with underscores) and use that
helper when building filename (reference variables ip_address, safe_ip,
filename, and the surrounding filename creation logic) so future callers can
reuse centralized, hardened rules.

388-395: Duplicated filename construction across save_results() and the finally block.

The IP sanitization + filename + abs_path logic at lines 388-395 is repeated almost verbatim at lines 529-534. Factor it out into a small helper (e.g., _results_filename()) to keep them in lock-step — otherwise a future change to the sanitization rule (e.g., also replacing / or handling IPv6 brackets) will need to be made in two places.

♻️ Suggested helper
+def _results_filename():
+    ip_address = bitaxe_ip.replace('http://', '')
+    safe_ip = ip_address.replace(':', '_')
+    return f"bitaxe_benchmark_results_{safe_ip}_{START_TIME}.json"
+
 def save_results():
     try:
-        # Extract IP from bitaxe_ip global variable and remove 'http://'
-        ip_address = bitaxe_ip.replace('http://', '')
-        # replace characters not valid in filenames on some OS (e.g., ':' on Windows)
-        safe_ip = ip_address.replace(':', '_')
-        filename = f"bitaxe_benchmark_results_{safe_ip}_{START_TIME}.json"
+        filename = _results_filename()

Also applies to: 529-534

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bitaxe_hashrate_benchmark.py` around lines 388 - 395, Duplicate filename
construction and path-printing logic appears in save_results() and the finally
block; extract this into a single helper (suggestion: _results_filename or
_save_results_file) that accepts ip_address, START_TIME and results (or just
returns the sanitized filename and absolute path), centralizes the sanitization
(e.g., safe_ip = ip_address.replace(':','_') and any future rules), performs the
file write and returns the filename/abs_path (or just returns filename and let
callers write if preferred), then replace the duplicated code in save_results()
and the finally block with calls to that helper to generate/write the file and
print the same messages; update references to save_results(), the finally block,
and use the new helper name consistently.
README.md (2)

93-93: flask is installed ad-hoc and not pinned.

pip install flask here picks up whatever the latest version is, which can drift across contributor machines. Consider adding flask to a requirements-dev.txt (or requirements.txt if you want it bundled) so the mock's behavior is reproducible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 93, Replace the ad-hoc "pip install flask" in the README
with a reproducible dependency declaration: add "Flask==<pin-version>" to your
requirements-dev.txt (or requirements.txt if preferred) and update the README to
instruct users to run "pip install -r requirements-dev.txt"; ensure the added
requirements file contains the exact pinned version string (e.g., Flask==2.2.5)
so the mock behavior is consistent across machines.

221-221: Endpoint list omits GET /api/hashrate.

The mock also exposes a legacy GET /api/hashrate endpoint (mock_bitaxe.py line 81). Either drop it from the mock if truly unused, or list it here for completeness so readers don't wonder why it's in the file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 221, The README endpoint list omits the legacy GET
/api/hashrate endpoint that the mock still exposes; either remove the legacy
handler from the mock or add GET /api/hashrate to the README list. Locate the
mock by searching for the string "GET /api/hashrate" in the codebase, then
either delete the handler or update the README entry (keeping wording that it is
a legacy endpoint if you retain it).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bitaxe_hashrate_benchmark.py`:
- Around line 70-73: The quick-mode sample budget is too small: with
benchmark_time=35 and sample_interval=5 you get 7 samples which, after the
trimming in benchmark_iteration (removing 3 highest and 3 lowest hashrates and 6
lowest temperatures), leaves only one sample or zero; update the quick-mode
behavior by either increasing the budget (e.g., set benchmark_time to 50 with
sample_interval 5 to yield 10 samples) or modify benchmark_iteration to detect
args.quick and skip/relax the aggressive trimming (or guard against empty
trimmed lists) and add a clarifying comment next to the args.quick block and the
trimming code to explain the dependency so future edits won’t silently break the
math.

In `@mock_bitaxe.py`:
- Around line 86-87: The app is currently bound to 0.0.0.0 in the __main__ block
via app.run(host="0.0.0.0", port=5000); change this to bind to localhost by
default (host="127.0.0.1") and add an optional override via an environment
variable (e.g., read os.getenv("MOCK_HOST", "127.0.0.1")) so callers can
explicitly opt into external exposure; keep the app.run(...) call but replace
its host arg with the env-driven value and update README/inline comment to note
that Docker on Linux may need extra flags to reach a host-bound loopback if you
choose to expose externally.
- Around line 59-71: The PATCH handler update_system_settings currently calls
int(...) on request values and will raise ValueError/TypeError for non-numeric
or missing inputs, causing a 500; modify update_system_settings to
validate/coerce inputs from request.get_json() before assigning to
state["coreVoltage"] and state["frequency"] — e.g., check for presence, attempt
int conversion inside a try/except (catch ValueError/TypeError) and either
ignore the bad field (leave existing state) or return a 400 JSON error response;
ensure no uncaught exceptions propagate so malformed payloads do not produce a
500.

In `@README.md`:
- Around line 215-222: Update the README example to distinguish Docker Desktop
vs native Linux usage so Linux users get a working example: keep the original
build step, then show two run variants — one using host.docker.internal:5000 for
Docker Desktop (Windows/macOS) and another using
--add-host=host.docker.internal:host-gateway for native Linux; mention that the
mock implements GET /api/system/info, GET /api/system/asic, PATCH /api/system,
POST /api/system/restart and that host.docker.internal may require the extra
flag on Linux so readers copying the original docker run command won’t see
connection failures.
- Around line 88-102: Update the "Quick Test" block in the README so it is
cross-platform like "Safe Mock Mode": keep the Windows PowerShell commands that
reference venv\Scripts\activate and mock_bitaxe.py, and add the equivalent
macOS/Linux commands using python -m venv venv and source venv/bin/activate (or
the POSIX activation) followed by pip install flask and python mock_bitaxe.py;
ensure the section heading or inline comments clarify which commands are for
Windows vs macOS/Linux so users running bitaxe_hashrate_benchmark.py --quick
against 127.0.0.1:5000 can follow the correct activation steps.

---

Nitpick comments:
In @.gitignore:
- Around line 174-175: The .gitignore currently contains a project-wide rule
ignoring "mock_bitaxe_OLD.py" which is a local, developer-specific artifact;
remove the "mock_bitaxe_OLD.py" entry from the shared .gitignore and either
delete the file (history preserves it) or add the filename to your personal
exclude file (.git/info/exclude) instead so the ignore stays local; update the
repository by committing the .gitignore removal and, if you keep the file
locally, add it to your .git/info/exclude on your machine.
- Line 41: Remove the duplicate ignore pattern "*benchmark_results*.json" from
the "Unit test / coverage reports" section in .gitignore so it only exists under
the correct "Benchmark results" section; locate the occurrence of
"*benchmark_results*.json" near the top of the file (in the unit test/coverage
block) and delete that entry, keeping the existing pattern in the benchmark
section intact.

In `@bitaxe_hashrate_benchmark.py`:
- Around line 98-100: The current quick-mode mutates the safety variable
max_allowed_frequency by setting it to initial_frequency when args.quick is
true; instead, avoid changing max_allowed_frequency and implement a separate
mechanism such as a quick_iteration_limit flag or break-after-first-success
logic: introduce a new boolean or counter (e.g., quick_iteration_limit or
did_one_iteration) and, inside the main loop that uses current_frequency and
frequency_increment, check args.quick and break after completing the first
successful iteration (or decrement the counter) rather than assigning
initial_frequency to max_allowed_frequency so diagnostic logs of
max_allowed_frequency remain correct.
- Around line 529-538: The current filename sanitization only replaces ':' via
safe_ip = ip_address.replace(':', '_'), which misses characters like '[' and ']'
(IPv6) and other potentially problematic chars; update the sanitization to a
reusable helper (e.g., create a sanitize_filename or sanitize_ip helper used
where safe_ip is set) that replaces or strips characters not allowed/desired in
filenames (at minimum replace ':' '[' ']' and any path separators like '/' '\'
with underscores) and use that helper when building filename (reference
variables ip_address, safe_ip, filename, and the surrounding filename creation
logic) so future callers can reuse centralized, hardened rules.
- Around line 388-395: Duplicate filename construction and path-printing logic
appears in save_results() and the finally block; extract this into a single
helper (suggestion: _results_filename or _save_results_file) that accepts
ip_address, START_TIME and results (or just returns the sanitized filename and
absolute path), centralizes the sanitization (e.g., safe_ip =
ip_address.replace(':','_') and any future rules), performs the file write and
returns the filename/abs_path (or just returns filename and let callers write if
preferred), then replace the duplicated code in save_results() and the finally
block with calls to that helper to generate/write the file and print the same
messages; update references to save_results(), the finally block, and use the
new helper name consistently.

In `@README.md`:
- Line 93: Replace the ad-hoc "pip install flask" in the README with a
reproducible dependency declaration: add "Flask==<pin-version>" to your
requirements-dev.txt (or requirements.txt if preferred) and update the README to
instruct users to run "pip install -r requirements-dev.txt"; ensure the added
requirements file contains the exact pinned version string (e.g., Flask==2.2.5)
so the mock behavior is consistent across machines.
- Line 221: The README endpoint list omits the legacy GET /api/hashrate endpoint
that the mock still exposes; either remove the legacy handler from the mock or
add GET /api/hashrate to the README list. Locate the mock by searching for the
string "GET /api/hashrate" in the codebase, then either delete the handler or
update the README entry (keeping wording that it is a legacy endpoint if you
retain it).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4d9833c3-71b5-414d-9bf8-98cd62fd0bcf

📥 Commits

Reviewing files that changed from the base of the PR and between 9ea86d6 and 815295b.

📒 Files selected for processing (4)
  • .gitignore
  • README.md
  • bitaxe_hashrate_benchmark.py
  • mock_bitaxe.py

Comment on lines +70 to +73
if args.quick:
sleep_time = 2
benchmark_time = 35
sample_interval = 5
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

--quick math is on the knife-edge of the validity check.

With benchmark_time=35 and sample_interval=5, total_samples = 35 // 5 = 7, which exactly meets the >= 7 guard at line 109. After the trimming logic in benchmark_iteration removes the 3 highest and 3 lowest hashrates (line 346) and the 6 lowest temperatures (line 351), the averages are computed from a single sample each — statistically meaningless and easy to break. Any future tweak to the trimming constants or this 35/5 will silently push --quick below the threshold or into a divide-by-zero on len(trimmed_temps).

Consider either bumping --quick to a slightly larger sample budget (e.g., benchmark_time=50, sample_interval=5 → 10 samples) or skipping/relaxing the trim logic when running in quick mode. At minimum, leave a comment so this trap is visible to future editors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bitaxe_hashrate_benchmark.py` around lines 70 - 73, The quick-mode sample
budget is too small: with benchmark_time=35 and sample_interval=5 you get 7
samples which, after the trimming in benchmark_iteration (removing 3 highest and
3 lowest hashrates and 6 lowest temperatures), leaves only one sample or zero;
update the quick-mode behavior by either increasing the budget (e.g., set
benchmark_time to 50 with sample_interval 5 to yield 10 samples) or modify
benchmark_iteration to detect args.quick and skip/relax the aggressive trimming
(or guard against empty trimmed lists) and add a clarifying comment next to the
args.quick block and the trimming code to explain the dependency so future edits
won’t silently break the math.

Comment thread mock_bitaxe.py
Comment on lines +59 to +71
@app.patch("/api/system")
def update_system_settings():
body = request.get_json(silent=True) or {}
# accept and store the requested settings
if "coreVoltage" in body:
state["coreVoltage"] = int(body["coreVoltage"])
if "frequency" in body:
state["frequency"] = int(body["frequency"])
# return the new state
return jsonify({
"coreVoltage": state["coreVoltage"],
"frequency": state["frequency"]
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

PATCH handler will 500 on non-numeric input.

int(body["coreVoltage"]) / int(body["frequency"]) raises ValueError/TypeError for non-numeric or None payloads, which Flask turns into a 500. For a mock that should "always succeed", consider validating types and returning a 400 (or silently coercing/ignoring), so a malformed test payload doesn't masquerade as a server crash.

♻️ Suggested change
-    if "coreVoltage" in body:
-        state["coreVoltage"] = int(body["coreVoltage"])
-    if "frequency" in body:
-        state["frequency"] = int(body["frequency"])
+    try:
+        if "coreVoltage" in body:
+            state["coreVoltage"] = int(body["coreVoltage"])
+        if "frequency" in body:
+            state["frequency"] = int(body["frequency"])
+    except (TypeError, ValueError):
+        return jsonify({"error": "coreVoltage and frequency must be integers"}), 400
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mock_bitaxe.py` around lines 59 - 71, The PATCH handler
update_system_settings currently calls int(...) on request values and will raise
ValueError/TypeError for non-numeric or missing inputs, causing a 500; modify
update_system_settings to validate/coerce inputs from request.get_json() before
assigning to state["coreVoltage"] and state["frequency"] — e.g., check for
presence, attempt int conversion inside a try/except (catch
ValueError/TypeError) and either ignore the bad field (leave existing state) or
return a 400 JSON error response; ensure no uncaught exceptions propagate so
malformed payloads do not produce a 500.

Comment thread mock_bitaxe.py
Comment on lines +86 to +87
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Bind the mock to localhost by default.

host="0.0.0.0" exposes the mock on every network interface, which is unnecessary for a local test fixture and is flagged by static analysis (Ruff S104 / CWE-668). Prefer binding to 127.0.0.1 and only expose externally when explicitly opted into (e.g., via env var) — the Docker workflow in the README already uses host.docker.internal to reach the host, which works with a loopback bind on Docker Desktop but not on Linux without extra flags, so document accordingly if you intend to keep 0.0.0.0.

🔒️ Suggested change
 if __name__ == "__main__":
-    app.run(host="0.0.0.0", port=5000)
+    import os
+    host = os.environ.get("MOCK_BITAXE_HOST", "127.0.0.1")
+    app.run(host=host, port=5000)
🧰 Tools
🪛 ast-grep (0.42.1)

[warning] 86-86: Running flask app with host 0.0.0.0 could expose the server publicly.
Context: app.run(host="0.0.0.0", port=5000)
Note: [CWE-668]: Exposure of Resource to Wrong Sphere [OWASP A01:2021]: Broken Access Control [REFERENCES]
https://owasp.org/Top10/A01_2021-Broken_Access_Control

(avoid_app_run_with_bad_host-python)

🪛 Ruff (0.15.11)

[error] 87-87: Possible binding to all interfaces

(S104)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mock_bitaxe.py` around lines 86 - 87, The app is currently bound to 0.0.0.0
in the __main__ block via app.run(host="0.0.0.0", port=5000); change this to
bind to localhost by default (host="127.0.0.1") and add an optional override via
an environment variable (e.g., read os.getenv("MOCK_HOST", "127.0.0.1")) so
callers can explicitly opt into external exposure; keep the app.run(...) call
but replace its host arg with the env-driven value and update README/inline
comment to note that Docker on Linux may need extra flags to reach a host-bound
loopback if you choose to expose externally.

Comment thread README.md
Comment on lines +88 to +102
1) Start the mock API (new shell):

```bash
python -m venv venv
venv\Scripts\activate
pip install flask
python mock_bitaxe.py
```

2) In another shell, run a short smoke test (≥7 samples but fast):

```bash
venv\Scripts\activate
python bitaxe_hashrate_benchmark.py 127.0.0.1:5000 --quick
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Quick Test instructions are Windows-only.

The "Safe Mock Mode" section (lines 199-211) properly shows both Windows and macOS/Linux activation, but this earlier "Quick Test" block only shows venv\Scripts\activate. Mirror the cross-platform example here so non-Windows users aren't left to figure it out.

📝 Suggested change
 ```bash
+# Windows (PowerShell)
 python -m venv venv
 venv\Scripts\activate
 pip install flask
 python mock_bitaxe.py
+
+# macOS/Linux
+# python -m venv venv
+# source venv/bin/activate
+# pip install flask
+# python mock_bitaxe.py

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @README.md around lines 88 - 102, Update the "Quick Test" block in the README
so it is cross-platform like "Safe Mock Mode": keep the Windows PowerShell
commands that reference venv\Scripts\activate and mock_bitaxe.py, and add the
equivalent macOS/Linux commands using python -m venv venv and source
venv/bin/activate (or the POSIX activation) followed by pip install flask and
python mock_bitaxe.py; ensure the section heading or inline comments clarify
which commands are for Windows vs macOS/Linux so users running
bitaxe_hashrate_benchmark.py --quick against 127.0.0.1:5000 can follow the
correct activation steps.


</details>

<!-- fingerprinting:phantom:poseidon:nectarine:53fc0bda-7a36-4484-b020-12f70c72c9ab -->

<!-- d98c2f50 -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment thread README.md
Comment on lines +215 to +222
```bash
docker build -t bitaxe-benchmark .
docker run --rm bitaxe-benchmark host.docker.internal:5000
```

Notes:
- The mock implements the endpoints the script calls: `GET /api/system/info`, `GET /api/system/asic`, `PATCH /api/system`, `POST /api/system/restart`.
- Host `host.docker.internal` is commonly available on Docker Desktop to reach the host from a container.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

host.docker.internal is broken on Linux without an extra flag.

On Docker Desktop (Windows/macOS) host.docker.internal resolves automatically, but on native Linux Docker it does not — users need --add-host=host.docker.internal:host-gateway for the example to work. The note hedges with "commonly available", but Linux contributors copy-pasting this command will get a connection failure.

📝 Suggested change
 ```bash
 docker build -t bitaxe-benchmark .
-docker run --rm bitaxe-benchmark host.docker.internal:5000
+# Docker Desktop (Windows/macOS):
+docker run --rm bitaxe-benchmark host.docker.internal:5000
+# Linux (native Docker):
+docker run --rm --add-host=host.docker.internal:host-gateway bitaxe-benchmark host.docker.internal:5000

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @README.md around lines 215 - 222, Update the README example to distinguish
Docker Desktop vs native Linux usage so Linux users get a working example: keep
the original build step, then show two run variants — one using
host.docker.internal:5000 for Docker Desktop (Windows/macOS) and another using
--add-host=host.docker.internal:host-gateway for native Linux; mention that the
mock implements GET /api/system/info, GET /api/system/asic, PATCH /api/system,
POST /api/system/restart and that host.docker.internal may require the extra
flag on Linux so readers copying the original docker run command won’t see
connection failures.


</details>

<!-- fingerprinting:phantom:poseidon:nectarine:53fc0bda-7a36-4484-b020-12f70c72c9ab -->

<!-- 4e71b3a2 -->

<!-- This is an auto-generated comment by CodeRabbit -->

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant