Compare commits
62 commits
miner-v0.1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d651a8dfb4 | ||
|
|
3aea1d7d1a | ||
|
|
75a9ba9ef4 | ||
|
|
b4f9e2439d | ||
|
|
6cc7caa17d | ||
|
|
1f464a62f1 | ||
|
|
8e1405e2a4 | ||
|
|
0a4480d951 | ||
|
|
2621e89c68 | ||
|
|
99e18ee24c | ||
|
|
51ff365815 | ||
|
|
2ee0e7d342 | ||
|
|
9cf90c4b36 | ||
|
|
ac354b1971 | ||
|
|
6ef566b059 | ||
|
|
655665a45c | ||
|
|
248554b364 | ||
|
|
e6016088ec | ||
|
|
1aeba5f825 | ||
|
|
813af77e4b | ||
|
|
bd2e8b20f8 | ||
|
|
cabdfb6d90 | ||
|
|
0da69ad713 | ||
|
|
3b52ddc437 | ||
|
|
072faeac5a | ||
|
|
10267c8274 | ||
|
|
37ea15e65e | ||
|
|
770e034786 | ||
|
|
73e81b793f | ||
|
|
ab78431606 | ||
|
|
e89d182480 | ||
|
|
f788d30bd6 | ||
|
|
97e9f02561 | ||
|
|
03efad84c3 | ||
|
|
c0ca900938 | ||
|
|
f6e2db8e80 | ||
|
|
644e749577 | ||
|
|
af7d133c02 | ||
|
|
f8f6316fb3 | ||
|
|
aa80b771ad | ||
|
|
28cbb006f5 | ||
|
|
8881dd324b | ||
|
|
33297e83a7 | ||
|
|
83f8538134 | ||
|
|
d1d5b5a219 | ||
|
|
23d3447909 | ||
|
|
da7e31e741 | ||
|
|
092d3a7da6 | ||
|
|
f91f251afc | ||
|
|
aa5c5b27f9 | ||
|
|
c75c73a1c0 | ||
|
|
4e90ffa7a9 | ||
|
|
f733254048 | ||
|
|
4a1d8058a5 | ||
|
|
196423a8fc | ||
|
|
52f3d98906 | ||
|
|
3e778f48fe | ||
|
|
bce309b78d | ||
| 9f1ec97a60 | |||
| 775c35c772 | |||
| dbd36374b2 | |||
| 6ffcb353c0 |
39 changed files with 8616 additions and 4551 deletions
251
.github/workflows/miner-release.yml
vendored
Normal file
251
.github/workflows/miner-release.yml
vendored
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
name: Miner Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'miner-v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version tag (e.g., 0.1.0)'
|
||||
required: true
|
||||
default: '0.1.0'
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
name: Linux ${{ matrix.arch }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x64
|
||||
cmake_arch: x86_64
|
||||
- arch: arm64
|
||||
cmake_arch: aarch64
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
libuv1-dev \
|
||||
libssl-dev \
|
||||
libhwloc-dev \
|
||||
git
|
||||
|
||||
- name: Build miner core
|
||||
working-directory: miner/core
|
||||
run: |
|
||||
mkdir -p build && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DWITH_OPENCL=OFF \
|
||||
-DWITH_CUDA=OFF
|
||||
cmake --build . --config $BUILD_TYPE -j$(nproc)
|
||||
|
||||
- name: Build miner proxy
|
||||
working-directory: miner/proxy
|
||||
run: |
|
||||
mkdir -p build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
cmake --build . --config $BUILD_TYPE -j$(nproc)
|
||||
|
||||
- name: Package binaries
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp miner/core/build/miner dist/
|
||||
cp miner/proxy/build/miner-proxy dist/
|
||||
chmod +x dist/*
|
||||
cd dist
|
||||
tar -czvf ../miner-linux-${{ matrix.arch }}.tar.gz *
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: miner-linux-${{ matrix.arch }}
|
||||
path: miner-linux-${{ matrix.arch }}.tar.gz
|
||||
|
||||
build-macos:
|
||||
name: macOS ${{ matrix.arch }}
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x64
|
||||
cmake_osx_arch: x86_64
|
||||
- arch: arm64
|
||||
cmake_osx_arch: arm64
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install cmake libuv openssl hwloc
|
||||
|
||||
- name: Build miner core
|
||||
working-directory: miner/core
|
||||
env:
|
||||
OSX_ARCH: ${{ matrix.cmake_osx_arch }}
|
||||
run: |
|
||||
mkdir -p build && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_OSX_ARCHITECTURES=$OSX_ARCH \
|
||||
-DWITH_OPENCL=OFF \
|
||||
-DWITH_CUDA=OFF \
|
||||
-DOPENSSL_ROOT_DIR=$(brew --prefix openssl)
|
||||
cmake --build . --config $BUILD_TYPE -j$(sysctl -n hw.ncpu)
|
||||
|
||||
- name: Build miner proxy
|
||||
working-directory: miner/proxy
|
||||
env:
|
||||
OSX_ARCH: ${{ matrix.cmake_osx_arch }}
|
||||
run: |
|
||||
mkdir -p build && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_OSX_ARCHITECTURES=$OSX_ARCH \
|
||||
-DOPENSSL_ROOT_DIR=$(brew --prefix openssl)
|
||||
cmake --build . --config $BUILD_TYPE -j$(sysctl -n hw.ncpu)
|
||||
|
||||
- name: Package binaries
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp miner/core/build/miner dist/
|
||||
cp miner/proxy/build/miner-proxy dist/
|
||||
chmod +x dist/*
|
||||
cd dist
|
||||
tar -czvf ../miner-macos-${{ matrix.arch }}.tar.gz *
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: miner-macos-${{ matrix.arch }}
|
||||
path: miner-macos-${{ matrix.arch }}.tar.gz
|
||||
|
||||
build-windows:
|
||||
name: Windows x64
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup MSVC
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
vcpkg install libuv:x64-windows openssl:x64-windows
|
||||
|
||||
- name: Build miner core
|
||||
working-directory: miner/core
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. `
|
||||
-DCMAKE_BUILD_TYPE=$env:BUILD_TYPE `
|
||||
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" `
|
||||
-DWITH_OPENCL=OFF `
|
||||
-DWITH_CUDA=OFF
|
||||
cmake --build . --config $env:BUILD_TYPE
|
||||
|
||||
- name: Build miner proxy
|
||||
working-directory: miner/proxy
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. `
|
||||
-DCMAKE_BUILD_TYPE=$env:BUILD_TYPE `
|
||||
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"
|
||||
cmake --build . --config $env:BUILD_TYPE
|
||||
|
||||
- name: Package binaries
|
||||
run: |
|
||||
mkdir dist
|
||||
Copy-Item miner/core/build/$env:BUILD_TYPE/miner.exe dist/
|
||||
Copy-Item miner/proxy/build/$env:BUILD_TYPE/miner-proxy.exe dist/
|
||||
Compress-Archive -Path dist/* -DestinationPath miner-windows-x64.zip
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: miner-windows-x64
|
||||
path: miner-windows-x64.zip
|
||||
|
||||
release:
|
||||
name: Create Release
|
||||
needs: [build-linux, build-macos, build-windows]
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: List artifacts
|
||||
run: ls -la artifacts/*/
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: |
|
||||
artifacts/miner-linux-x64/miner-linux-x64.tar.gz
|
||||
artifacts/miner-linux-arm64/miner-linux-arm64.tar.gz
|
||||
artifacts/miner-macos-x64/miner-macos-x64.tar.gz
|
||||
artifacts/miner-macos-arm64/miner-macos-arm64.tar.gz
|
||||
artifacts/miner-windows-x64/miner-windows-x64.zip
|
||||
body: |
|
||||
## Miner Suite
|
||||
|
||||
### Downloads
|
||||
|
||||
| Platform | Architecture | Download |
|
||||
|----------|--------------|----------|
|
||||
| Linux | x64 | `miner-linux-x64.tar.gz` |
|
||||
| Linux | ARM64 | `miner-linux-arm64.tar.gz` |
|
||||
| macOS | Intel | `miner-macos-x64.tar.gz` |
|
||||
| macOS | Apple Silicon | `miner-macos-arm64.tar.gz` |
|
||||
| Windows | x64 | `miner-windows-x64.zip` |
|
||||
|
||||
### Included Binaries
|
||||
|
||||
- `miner` - CPU/GPU cryptocurrency miner
|
||||
- `miner-proxy` - Stratum proxy for mining farms
|
||||
|
||||
### Quick Start
|
||||
|
||||
```bash
|
||||
# Extract
|
||||
tar -xzf miner-linux-x64.tar.gz
|
||||
|
||||
# Run miner
|
||||
./miner -o pool.example.com:3333 -u YOUR_WALLET -p x
|
||||
|
||||
# Run proxy
|
||||
./miner-proxy -o pool.example.com:3333 -u YOUR_WALLET -b 0.0.0.0:3333
|
||||
```
|
||||
|
||||
See [miner/README.md](miner/README.md) for full documentation.
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
127
AUDIT-COMPLEXITY.md
Normal file
127
AUDIT-COMPLEXITY.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Code Complexity and Maintainability Audit
|
||||
|
||||
This document analyzes the code quality of the codebase, identifies maintainability issues, and provides recommendations for improvement. The audit focuses on cyclomatic and cognitive complexity, code duplication, and other maintainability metrics.
|
||||
|
||||
## 1. God Class: `Manager`
|
||||
|
||||
### Finding
|
||||
The `Manager` struct in `pkg/mining/manager.go` is a "God Class" that violates the Single Responsibility Principle. It handles multiple, unrelated responsibilities, including:
|
||||
- Miner lifecycle management (`StartMiner`, `StopMiner`)
|
||||
- Configuration management (`syncMinersConfig`, `updateMinerConfig`)
|
||||
- Database interactions (`initDatabase`, `startDBCleanup`)
|
||||
- Statistics collection (`startStatsCollection`, `collectMinerStats`)
|
||||
|
||||
This centralization of concerns makes the `Manager` class difficult to understand, test, and maintain. The presence of multiple mutexes (`mu`, `eventHubMu`) to prevent deadlocks is a clear indicator of its high cognitive complexity.
|
||||
|
||||
### Recommendation
|
||||
Refactor the `Manager` class into smaller, more focused components, each with a single responsibility.
|
||||
|
||||
- **`MinerRegistry`**: Manages the lifecycle of miner instances.
|
||||
- **`StatsCollector`**: Gathers and aggregates statistics from miners.
|
||||
- **`ConfigService`**: Handles loading, saving, and updating miner configurations.
|
||||
- **`DBManager`**: Manages all database-related operations.
|
||||
|
||||
This separation of concerns will improve modularity, reduce complexity, and make the system easier to reason about and test.
|
||||
|
||||
## 2. Code Duplication: Miner Installation
|
||||
|
||||
### Finding
|
||||
The `Install` and `CheckInstallation` methods in `pkg/mining/xmrig.go` and `pkg/mining/ttminer.go` contain nearly identical logic for downloading, extracting, and verifying miner installations. This copy-paste pattern violates the DRY (Don't Repeat Yourself) principle and creates a significant maintenance burden. Any change to the installation process must be manually duplicated across all miner implementations.
|
||||
|
||||
### Recommendation
|
||||
Refactor the duplicated logic into the `BaseMiner` struct using the **Template Method Pattern**. The base struct will define the skeleton of the installation algorithm, while subclasses will override specific steps (like providing the download URL format) that vary between miners.
|
||||
|
||||
#### Example
|
||||
The `BaseMiner` can provide a generic `Install` method that relies on a new, unexported method, `getDownloadURL`, which each miner implementation must provide.
|
||||
|
||||
**`pkg/mining/miner.go` (BaseMiner)**
|
||||
```go
|
||||
// Install orchestrates the download and extraction process.
|
||||
func (b *BaseMiner) Install() error {
|
||||
version, err := b.GetLatestVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Version = version
|
||||
|
||||
url, err := b.getDownloadURL(version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return b.InstallFromURL(url)
|
||||
}
|
||||
|
||||
// getDownloadURL is a template method to be implemented by subclasses.
|
||||
func (b *BaseMiner) getDownloadURL(version string) (string, error) {
|
||||
// This will be overridden by specific miner types
|
||||
return "", errors.New("getDownloadURL not implemented")
|
||||
}
|
||||
```
|
||||
|
||||
**`pkg/mining/xmrig.go` (XMRigMiner)**
|
||||
```go
|
||||
// getDownloadURL implements the template method for XMRig.
|
||||
func (m *XMRigMiner) getDownloadURL(version string) (string, error) {
|
||||
v := strings.TrimPrefix(version, "v")
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return fmt.Sprintf("https://.../xmrig-%s-windows-x64.zip", v), nil
|
||||
case "linux":
|
||||
return fmt.Sprintf("https://.../xmrig-%s-linux-static-x64.tar.gz", v), nil
|
||||
default:
|
||||
return "", errors.New("unsupported OS")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Long and Complex Methods
|
||||
|
||||
### Finding
|
||||
Several methods in the codebase are overly long and have high cognitive complexity, making them difficult to read, understand, and maintain.
|
||||
|
||||
- **`manager.StartMiner`**: This method is responsible for creating, configuring, and starting a miner. It mixes validation, port finding, instance name generation, and state management, making it hard to follow.
|
||||
- **`manager.collectMinerStats`**: This function orchestrates the parallel collection of stats, but the logic for handling timeouts, retries, and database persistence is deeply nested.
|
||||
- **`miner.ReduceHashrateHistory`**: The logic for aggregating high-resolution hashrate data into a low-resolution format is convoluted and hard to reason about.
|
||||
|
||||
### Recommendation
|
||||
Apply the **Extract Method** refactoring to break down these long methods into smaller, well-named functions, each with a single, clear purpose.
|
||||
|
||||
#### Example: Refactoring `manager.StartMiner`
|
||||
The `StartMiner` method could be refactored into several smaller helper functions.
|
||||
|
||||
**`pkg/mining/manager.go` (Original `StartMiner`)**
|
||||
```go
|
||||
func (m *Manager) StartMiner(ctx context.Context, minerType string, config *Config) (Miner, error) {
|
||||
// ... (20+ lines of setup, validation, port finding)
|
||||
|
||||
// ... (10+ lines of miner-specific configuration)
|
||||
|
||||
// ... (10+ lines of starting and saving logic)
|
||||
}
|
||||
```
|
||||
|
||||
**`pkg/mining/manager.go` (Refactored `StartMiner`)**
|
||||
```go
|
||||
func (m *Manager) StartMiner(ctx context.Context, minerType string, config *Config) (Miner, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceName, err := m.generateInstanceName(minerType, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
miner, err := m.configureMiner(minerType, instanceName, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := m.launchAndRegisterMiner(miner, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return miner, nil
|
||||
}
|
||||
```
|
||||
60
AUDIT-CONCURRENCY.md
Normal file
60
AUDIT-CONCURRENCY.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Concurrency and Race Condition Audit
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
This audit examined the concurrency safety of the mining operations within the `pkg/mining` package. The assessment involved a combination of automated race detection using `go test -race` and a manual code review of the key components responsible for managing miner lifecycles and statistics collection.
|
||||
|
||||
**The primary finding is that the core concurrency logic is well-designed and appears to be free of race conditions.** The code demonstrates a strong understanding of Go's concurrency patterns, with proper use of mutexes to protect shared state.
|
||||
|
||||
The most significant risk identified is the **lack of complete test coverage** for code paths that interact with live miner processes. This limitation prevented the Go race detector from analyzing these sections, leaving a gap in the automated verification.
|
||||
|
||||
## 2. Methodology
|
||||
|
||||
The audit was conducted in two phases:
|
||||
|
||||
1. **Automated Race Detection**: The test suite for the `pkg/mining` package was executed with the `-race` flag enabled (`go test -race ./pkg/mining/...`). This tool instrumented the code to detect and report any data races that occurred during the execution of the tests.
|
||||
2. **Manual Code Review**: A thorough manual inspection of the source code was performed, focusing on `manager.go`, `miner.go`, and the `xmrig` and `ttminer` implementations. The review prioritized areas with shared mutable state, goroutine management, and I/O operations.
|
||||
|
||||
## 3. Findings
|
||||
|
||||
### 3.1. Automated Race Detection (`go test -race`)
|
||||
|
||||
The Go race detector **did not report any race conditions** in the code paths that were executed by the test suite. This provides a good level of confidence in the concurrency safety of the `Manager`'s core logic for adding, removing, and listing miners, as these operations are well-covered by the existing tests.
|
||||
|
||||
However, a number of tests related to live miner interaction (e.g., `TestCPUThrottleSingleMiner`) were skipped because they require the `xmrig` binary to be present in the test environment. As a result, the race detector could not analyze the code executed in these tests.
|
||||
|
||||
### 3.2. Manual Code Review
|
||||
|
||||
The manual review confirmed the findings of the race detector and extended the analysis to the code paths that were not covered by the tests.
|
||||
|
||||
#### 3.2.1. `Manager` (`manager.go`)
|
||||
|
||||
* **Shared State**: The `miners` map is the primary shared resource.
|
||||
* **Protection**: A `sync.RWMutex` is used to protect all access to the `miners` map.
|
||||
* **Analysis**: The `collectMinerStats` function is the most critical concurrent operation. It correctly uses a read lock to create a snapshot of the active miners and then releases the lock before launching concurrent goroutines to collect stats from each miner. This is a robust pattern that minimizes lock contention and delegates thread safety to the individual `Miner` implementations. All other methods on the `Manager` use the mutex correctly.
|
||||
|
||||
#### 3.2.2. `BaseMiner` (`miner.go`)
|
||||
|
||||
* **Shared State**: The `BaseMiner` struct contains several fields that are accessed and modified concurrently, including `Running`, `cmd`, and `HashrateHistory`.
|
||||
* **Protection**: A `sync.RWMutex` is used to protect all shared fields.
|
||||
* **Analysis**: Methods like `Stop`, `AddHashratePoint`, and `ReduceHashrateHistory` correctly acquire and release the mutex. The locking is fine-grained and properly scoped.
|
||||
|
||||
#### 3.2.3. `XMRigMiner` and `TTMiner`
|
||||
|
||||
* **`GetStats` Method**: This is the most important method for concurrency in the miner implementations. Both `XMRigMiner` and `TTMiner` follow an excellent pattern:
|
||||
1. Acquire a read lock to safely read the API configuration.
|
||||
2. Release the lock *before* making the blocking HTTP request.
|
||||
3. After the request completes, acquire a write lock to update the `FullStats` field.
|
||||
This prevents holding a lock during a potentially long I/O operation, which is a common cause of performance bottlenecks and deadlocks.
|
||||
* **`Start` Method**: Both implementations launch a goroutine to wait for the miner process to exit. This goroutine correctly captures a local copy of the `exec.Cmd` pointer. When updating the `Running` and `cmd` fields after the process exits, it checks if the current `m.cmd` is still the same as the one it was started with. This correctly handles the case where a miner might be stopped and restarted quickly, preventing the old goroutine from incorrectly modifying the state of the new process.
|
||||
|
||||
## 4. Conclusion and Recommendations
|
||||
|
||||
The mining operations in this codebase are implemented with a high degree of concurrency safety. The use of mutexes is consistent and correct, and the patterns used for handling I/O in concurrent contexts are exemplary.
|
||||
|
||||
The primary recommendation is to **improve the test coverage** to allow the Go race detector to provide a more complete analysis.
|
||||
|
||||
* **Recommendation 1 (High Priority)**: Modify the test suite to use a mock or simulated miner process. The existing tests already use a dummy script for some installation checks. This could be extended to create a mock HTTP server that simulates the miner's API. This would allow the skipped tests to run, enabling the race detector to analyze the `GetStats` methods and other live interaction code paths.
|
||||
* **Recommendation 2 (Low Priority)**: The `httpClient` in `xmrig.go` is a global variable protected by a mutex. While the default `http.Client` is thread-safe, and the mutex provides protection for testing, it would be slightly cleaner to make the HTTP client a field on the `XMRigMiner` struct. This would avoid the global state and make the dependencies of the miner more explicit. However, this is a minor architectural point and not a critical concurrency issue.
|
||||
|
||||
Overall, the risk of race conditions in the current codebase is low, but shoring up the test suite would provide even greater confidence in its robustness.
|
||||
72
AUDIT-DOCUMENTATION.md
Normal file
72
AUDIT-DOCUMENTATION.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Documentation Audit Report
|
||||
|
||||
## README Assessment
|
||||
|
||||
| Category | Status | Notes |
|
||||
|---|---|---|
|
||||
| **Project Description** | ✅ Pass | The README provides a clear and concise description of the project's purpose. |
|
||||
| **Quick Start** | ✅ Pass | The "Quick Start" section is excellent, offering a Docker command for immediate setup. |
|
||||
| **Installation** | ✅ Pass | Multiple installation methods are documented (Docker, CLI, source). |
|
||||
| **Configuration** | ✅ Pass | Configuration is explained with a clear example of a JSON profile. |
|
||||
| **Examples** | ✅ Pass | The README includes usage examples for Docker, the CLI, and the web component. |
|
||||
| **Badges** | ✅ Pass | A comprehensive set of badges is present, covering build status, coverage, and versioning. |
|
||||
|
||||
**Overall:** The `README.md` is comprehensive and user-friendly.
|
||||
|
||||
## Code Documentation
|
||||
|
||||
| Category | Status | Notes |
|
||||
|---|---|---|
|
||||
| **Function Docs** | ✅ Pass | Public APIs are well-documented with clear explanations. |
|
||||
| **Parameter Types** | ✅ Pass | Go's static typing ensures parameter types are documented. |
|
||||
| **Return Values** | ✅ Pass | Return values are documented in the function comments. |
|
||||
| **Examples** | ❌ Fail | There are no runnable examples in the Go docstrings. |
|
||||
| **Outdated Docs** | ✅ Pass | The documentation appears to be up-to-date with the code. |
|
||||
|
||||
**Overall:** The code is well-documented, but could be improved by adding runnable examples in the docstrings, which would be automatically included in the GoDoc.
|
||||
|
||||
## Architecture Documentation
|
||||
|
||||
| Category | Status | Notes |
|
||||
|---|---|---|
|
||||
| **System Overview** | ✅ Pass | `docs/ARCHITECTURE.md` provides a high-level overview of the system. |
|
||||
| **Data Flow** | ✅ Pass | The architecture document includes a sequence diagram illustrating data flow. |
|
||||
| **Component Diagram** | ✅ Pass | A Mermaid diagram visually represents the system's components. |
|
||||
| **Decision Records** | ❌ Fail | There are no Architecture Decision Records (ADRs) present. |
|
||||
|
||||
**Overall:** The architecture is well-documented, but would benefit from ADRs to track key decisions.
|
||||
|
||||
## Developer Documentation
|
||||
|
||||
| Category | Status | Notes |
|
||||
|---|---|---|
|
||||
| **Contributing Guide** | ✅ Pass | The `README.md` and `docs/DEVELOPMENT.md` provide clear contribution instructions. |
|
||||
| **Development Setup** | ✅ Pass | Prerequisites and setup steps are documented. |
|
||||
| **Testing Guide** | ✅ Pass | The `docs/DEVELOPMENT.md` file explains how to run tests. |
|
||||
| **Code Style** | 🟠 Partial | A formal code style guide is missing, but `make lint` and `make fmt` are provided. |
|
||||
|
||||
**Overall:** Developer documentation is good, but a formal style guide would be a useful addition.
|
||||
|
||||
## User Documentation
|
||||
|
||||
| Category | Status | Notes |
|
||||
|---|---|---|
|
||||
| **User Guide** | ✅ Pass | The MkDocs site serves as a comprehensive user guide. |
|
||||
| **FAQ** | ❌ Fail | A dedicated FAQ section is missing. |
|
||||
| **Troubleshooting** | ✅ Pass | A troubleshooting guide is available in the documentation. |
|
||||
| **Changelog** | ✅ Pass | `CHANGELOG.md` is present and well-maintained. |
|
||||
|
||||
**Overall:** User documentation is strong, but could be improved with a FAQ section.
|
||||
|
||||
## Summary of Documentation Gaps
|
||||
|
||||
The following documentation gaps have been identified:
|
||||
|
||||
- **Code Documentation:**
|
||||
- Add runnable examples to Go docstrings to improve GoDoc.
|
||||
- **Architecture Documentation:**
|
||||
- Introduce Architecture Decision Records (ADRs) to document key architectural decisions.
|
||||
- **Developer Documentation:**
|
||||
- Create a formal code style guide to ensure consistency.
|
||||
- **User Documentation:**
|
||||
- Add a Frequently Asked Questions (FAQ) section to the user guide.
|
||||
49
AUDIT-ERROR-HANDLING.md
Normal file
49
AUDIT-ERROR-HANDLING.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Error Handling and Logging Audit
|
||||
|
||||
## 1. Error Handling
|
||||
|
||||
### Exception Handling & Error Recovery
|
||||
- **Graceful Degradation**: The application demonstrates graceful degradation in `pkg/mining/service.go`, where the `NewService` function continues to operate with a minimal in-memory profile manager if the primary one fails to initialize. This ensures core functionality remains available.
|
||||
- **Inconsistent Top-Level Handling**: Error handling at the application's entry points is inconsistent.
|
||||
- In `cmd/desktop/mining-desktop/main.go`, errors from `fs.Sub` and `app.Run` are handled with `log.Fatal`, which abruptly terminates the application without using the project's structured logger.
|
||||
- In `cmd/mining/main.go`, errors from `cmd.Execute` are printed to `stderr` with `fmt.Fprintf`, and the application exits with a status code of 1. This is a standard CLI pattern but bypasses the custom logging framework.
|
||||
- **No Retry or Circuit Breaker Patterns**: The codebase does not currently implement explicit retry logic with backoff or circuit breaker patterns for handling failures in external dependencies or services. However, the API error response includes a `Retryable` field, which correctly signals to clients when a retry is appropriate (e.g., for `503 Service Unavailable`).
|
||||
|
||||
### User-Facing & API Errors
|
||||
- **Standard API Error Response**: The API service (`pkg/mining/service.go`) excels at providing consistent, user-friendly error responses.
|
||||
- It uses a well-defined `APIError` struct that includes a machine-readable `code`, a human-readable `message`, and an optional `suggestion` to guide the user.
|
||||
- The `respondWithError` and `respondWithMiningError` functions centralize error response logic, ensuring all API errors follow a consistent format.
|
||||
- **Appropriate HTTP Status Codes**: The API correctly maps application errors to standard HTTP status codes (e.g., `404 Not Found` for missing miners, `400 Bad Request` for invalid input, `500 Internal Server Error` for server-side issues).
|
||||
- **Controlled Information Leakage**: The `sanitizeErrorDetails` function prevents the leakage of sensitive internal error details in production environments (`GIN_MODE=release`), enhancing security. Debug information is only exposed when `DEBUG_ERRors` is enabled.
|
||||
|
||||
## 2. Logging
|
||||
|
||||
### Log Content and Quality
|
||||
- **Custom Structured Logger**: The project includes a custom logger in `pkg/logging/logger.go` that supports standard log levels (Debug, Info, Warn, Error) and allows for structured logging by attaching key-value fields.
|
||||
- **No JSON Output**: The logger's output is a custom string format (`timestamp [LEVEL] [component] message | key=value`), not structured JSON. This makes logs less machine-readable and harder to parse, filter, and analyze with modern log management tools.
|
||||
- **Good Context in Error Logs**: The existing usage of `logging.Error` throughout the `pkg/mining` module is effective, consistently including relevant context (e.g., `miner`, `panic`, `error`) as structured fields.
|
||||
- **Request Correlation**: The API service (`pkg/mining/service.go`) implements a `requestIDMiddleware` that assigns a unique `X-Request-ID` to each request, which is then included in logs. This is excellent practice for tracing and debugging.
|
||||
|
||||
### What is Not Logged
|
||||
- **No Sensitive Data**: Based on a review of `logging.Error` usage, the application appears to correctly avoid logging sensitive information such as passwords, tokens, or personally identifiable information (PII).
|
||||
|
||||
### Inconsistencies
|
||||
- **Inconsistent Adoption**: The custom logger is not used consistently across the project. The `main` packages for both the desktop and CLI applications (`cmd/desktop/mining-desktop/main.go` and `cmd/mining/main.go`) use the standard `log` and `fmt` packages for error handling, bypassing the structured logger.
|
||||
- **No Centralized Configuration**: There is no centralized logger initialization in `main` or `root.go`. The global logger is used with its default configuration (Info level, stderr output), and there is no clear mechanism for configuring the log level or output via command-line flags or a configuration file.
|
||||
|
||||
## 3. Recommendations
|
||||
|
||||
1. **Adopt Structured JSON Logging**: Modify the logger in `pkg/logging/logger.go` to output logs in JSON format. This will significantly improve the logs' utility by making them machine-readable and compatible with log analysis platforms like Splunk, Datadog, or the ELK stack.
|
||||
|
||||
2. **Centralize Logger Configuration**:
|
||||
* In `cmd/mining/cmd/root.go`, add persistent flags for `--log-level` and `--log-format` (e.g., `text`, `json`).
|
||||
* In an `init` function, parse these flags and configure the global `logging.Logger` instance accordingly.
|
||||
* Do the same for the desktop application in `cmd/desktop/mining-desktop/main.go`, potentially reading from a configuration file or environment variables.
|
||||
|
||||
3. **Standardize on the Global Logger**:
|
||||
* Replace all instances of `log.Fatal` in `cmd/desktop/mining-desktop/main.go` with `logging.Error` followed by `os.Exit(1)`.
|
||||
* Replace `fmt.Fprintf(os.Stderr, ...)` in `cmd/mining/main.go` with a call to `logging.Error`.
|
||||
|
||||
4. **Enrich API Error Logs**: In `pkg/mining/service.go`, enhance the `respondWithError` function to log every API error it handles using the structured logger. This will ensure that all error conditions, including client-side errors like bad requests, are recorded for monitoring and analysis. Include the `request_id` in every log entry.
|
||||
|
||||
5. **Review Log Levels**: Conduct a codebase-wide review of log levels. Ensure that `Debug` is used for verbose, development-time information, `Info` for significant operational events, `Warn` for recoverable issues, and `Error` for critical, action-required failures.
|
||||
204
AUDIT-INPUT-VALIDATION.md
Normal file
204
AUDIT-INPUT-VALIDATION.md
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
# Security Audit: Input Validation
|
||||
|
||||
This document outlines the findings of a security audit focused on input validation and sanitization within the mining application.
|
||||
|
||||
## Input Entry Points Inventory
|
||||
|
||||
### API Endpoints
|
||||
|
||||
The primary entry points for untrusted input are the API handlers defined in `pkg/mining/service.go`. The following handlers process user-controllable data from URL path parameters, query strings, and request bodies:
|
||||
|
||||
- **System & Miner Management:**
|
||||
- `POST /miners/:miner_name/install`: `miner_name` from path.
|
||||
- `DELETE /miners/:miner_name/uninstall`: `miner_name` from path.
|
||||
- `DELETE /miners/:miner_name`: `miner_name` from path.
|
||||
- `POST /miners/:miner_name/stdin`: `miner_name` from path and JSON body (`input`).
|
||||
|
||||
- **Statistics & History:**
|
||||
- `GET /miners/:miner_name/stats`: `miner_name` from path.
|
||||
- `GET /miners/:miner_name/hashrate-history`: `miner_name` from path.
|
||||
- `GET /miners/:miner_name/logs`: `miner_name` from path.
|
||||
- `GET /history/miners/:miner_name`: `miner_name` from path.
|
||||
- `GET /history/miners/:miner_name/hashrate`: `miner_name` from path, `since` and `until` from query.
|
||||
|
||||
- **Profiles:**
|
||||
- `POST /profiles`: JSON body (`MiningProfile`).
|
||||
- `GET /profiles/:id`: `id` from path.
|
||||
- `PUT /profiles/:id`: `id` from path and JSON body (`MiningProfile`).
|
||||
- `DELETE /profiles/:id`: `id` from path.
|
||||
- `POST /profiles/:id/start`: `id` from path.
|
||||
|
||||
### WebSocket Events
|
||||
|
||||
The WebSocket endpoint provides another significant entry point for untrusted input:
|
||||
|
||||
- **`GET /ws/events`**: Establishes a WebSocket connection. While the primary flow is server-to-client, the initial handshake and any client-to-server messages must be considered untrusted input. The `wsUpgrader` in `pkg/mining/service.go` has an origin check, which is a good security measure.
|
||||
|
||||
## Validation Gaps Found
|
||||
|
||||
The `Config.Validate()` method in `pkg/mining/mining.go` provides a solid baseline for input validation but has several gaps:
|
||||
|
||||
### Strengths
|
||||
|
||||
- **Core Fields Validated**: The most critical fields for command-line construction (`Pool`, `Wallet`, `Algo`, `CLIArgs`) have validation checks.
|
||||
- **Denylist for Shell Characters**: The `containsShellChars` function attempts to block a wide range of characters that could be used for shell injection.
|
||||
- **Range Checks**: Numeric fields like `Threads`, `Intensity`, and `DonateLevel` are correctly checked to ensure they fall within a sane range.
|
||||
- **Allowlist for Algorithm**: The `isValidAlgo` function uses a strict allowlist for the `Algo` field, which is a security best practice.
|
||||
|
||||
### Weaknesses and Gaps
|
||||
|
||||
- **Incomplete Field Coverage**: A significant number of fields in the `Config` struct are not validated at all. An attacker could potentially abuse these fields if they are used in command-line arguments or other sensitive operations in the future. Unvalidated fields include:
|
||||
- `Coin`
|
||||
- `Password`
|
||||
- `UserPass`
|
||||
- `Proxy`
|
||||
- `RigID`
|
||||
- `LogFile` (potential for path traversal)
|
||||
- `CPUAffinity`
|
||||
- `Devices`
|
||||
- Many others.
|
||||
|
||||
- **Denylist Approach**: The primary validation mechanism, `containsShellChars`, relies on a denylist of dangerous characters. This approach is inherently brittle because it is impossible to foresee all possible malicious inputs. A determined attacker might find ways to bypass the filter using alternative encodings or unlisted characters. An allowlist approach, accepting only known-good characters, is much safer.
|
||||
|
||||
- **No Path Traversal Protection**: The `LogFile` field is not validated. An attacker could provide a value like `../../../../etc/passwd` to attempt to write files in arbitrary locations on the filesystem.
|
||||
|
||||
- **Inconsistent Numeric Validation**: While some numeric fields are validated, others like `Retries`, `RetryPause`, `CPUPriority`, etc., are not checked for negative values or reasonable upper bounds.
|
||||
|
||||
## Injection Vectors Discovered
|
||||
|
||||
The primary injection vector discovered is through the `Config.CLIArgs` field, which is used to pass additional command-line arguments to the miner executables.
|
||||
|
||||
### XMRig Miner (`pkg/mining/xmrig_start.go`)
|
||||
|
||||
- **Unused in `xmrig_start.go`**: The `addCliArgs` function in `xmrig_start.go` does not actually use the `CLIArgs` field. It constructs arguments from other validated fields. This is good, but the presence of the field in the `Config` struct is misleading and could be used in the future, creating a vulnerability if not handled carefully.
|
||||
|
||||
### TT-Miner (`pkg/mining/ttminer_start.go`)
|
||||
|
||||
- **Direct Command Injection via `CLIArgs`**: The `addTTMinerCliArgs` function directly appends the contents of `Config.CLIArgs` to the command-line arguments. Although it uses a denylist-based `isValidCLIArg` function to filter out some dangerous characters, this approach is not foolproof.
|
||||
|
||||
- **Vulnerability**: An attacker can bypass the filter by crafting a malicious string that is not on the denylist but is still interpreted by the shell. For example, if a new shell feature or a different shell is used on the system, the denylist may become ineffective.
|
||||
|
||||
- **Example**: While the current filter blocks most common injection techniques, an attacker could still pass arguments that might cause unexpected behavior in the miner, such as `--algo some-exploitable-algo`, if the miner itself has vulnerabilities in how it parses certain arguments.
|
||||
|
||||
### Path Traversal in Config File Creation
|
||||
|
||||
- **Vulnerability**: The `getXMRigConfigPath` function in `xmrig.go` uses the `instanceName` to construct a config file path. The `instanceName` is derived from the user-provided `config.Algo`. While the `instanceNameRegex` in `manager.go` sanitizes the algorithm name, it still allows forward slashes (`/`).
|
||||
|
||||
- **Example**: If an attacker provides a crafted `algo` like `../../../../tmp/myconfig`, the `instanceNameRegex` will not sanitize it, and the application could write a config file to an arbitrary location. This could be used to overwrite critical files or place malicious configuration files in sensitive locations.
|
||||
|
||||
## Remediation Recommendations
|
||||
|
||||
To address the identified vulnerabilities, the following remediation actions are recommended:
|
||||
|
||||
### 1. Strengthen `Config.Validate()` with an Allowlist Approach
|
||||
|
||||
Instead of relying on a denylist of dangerous characters, the validation should be updated to use a strict allowlist of known-good characters for each field.
|
||||
|
||||
**Code Example (`pkg/mining/mining.go`):**
|
||||
\`\`\`go
|
||||
// isValidInput checks if a string contains only allowed characters.
|
||||
// This should be used for fields like Wallet, Password, Pool, etc.
|
||||
func isValidInput(s string, allowedChars string) bool {
|
||||
for _, r := range s {
|
||||
if !strings.ContainsRune(allowedChars, r) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// In Config.Validate():
|
||||
func (c *Config) Validate() error {
|
||||
// Example for Wallet field
|
||||
if c.Wallet != "" {
|
||||
// Allow alphanumeric, plus common address characters like '-' and '_'
|
||||
allowedChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
|
||||
if !isValidInput(c.Wallet, allowedChars) {
|
||||
return fmt.Errorf("wallet address contains invalid characters")
|
||||
}
|
||||
}
|
||||
|
||||
// Apply similar allowlist validation to all other string fields.
|
||||
// ...
|
||||
|
||||
return nil
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### 2. Sanitize File Paths to Prevent Path Traversal
|
||||
|
||||
Sanitize any user-controllable input that is used to construct file paths. The `filepath.Clean` function and checks to ensure the path stays within an expected directory are essential.
|
||||
|
||||
**Code Example (`pkg/mining/manager.go`):**
|
||||
\`\`\`go
|
||||
import "path/filepath"
|
||||
|
||||
// In Manager.StartMiner():
|
||||
// ...
|
||||
instanceName := miner.GetName()
|
||||
if config.Algo != "" {
|
||||
// Sanitize algo to prevent directory traversal
|
||||
sanitizedAlgo := instanceNameRegex.ReplaceAllString(config.Algo, "_")
|
||||
// Also, explicitly remove any path-related characters that the regex might miss
|
||||
sanitizedAlgo = strings.ReplaceAll(sanitizedAlgo, "/", "")
|
||||
sanitizedAlgo = strings.ReplaceAll(sanitizedAlgo, "..", "")
|
||||
instanceName = fmt.Sprintf("%s-%s", instanceName, sanitizedAlgo)
|
||||
}
|
||||
// ...
|
||||
\`\`\`
|
||||
|
||||
### 3. Avoid Passing Raw CLI Arguments to `exec.Command`
|
||||
|
||||
The `CLIArgs` field is inherently dangerous. If it must be supported, it should be parsed and validated argument by argument, rather than being passed directly to the shell.
|
||||
|
||||
**Code Example (`pkg/mining/ttminer_start.go`):**
|
||||
\`\`\`go
|
||||
// In addTTMinerCliArgs():
|
||||
func addTTMinerCliArgs(config *Config, args *[]string) {
|
||||
if config.CLIArgs != "" {
|
||||
// A safer approach is to define a list of allowed arguments
|
||||
allowedArgs := map[string]bool{
|
||||
"--list-devices": true,
|
||||
"--no-watchdog": true,
|
||||
// Add other safe, non-sensitive arguments here
|
||||
}
|
||||
|
||||
extraArgs := strings.Fields(config.CLIArgs)
|
||||
for _, arg := range extraArgs {
|
||||
if allowedArgs[arg] {
|
||||
*args = append(*args, arg)
|
||||
} else {
|
||||
logging.Warn("skipping potentially unsafe CLI argument", logging.Fields{"arg": arg})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### 4. Expand Validation Coverage in `Config.Validate()`
|
||||
|
||||
All fields in the `Config` struct should have some form of validation. For string fields, this should be allowlist-based character validation. For numeric fields, this should be range checking.
|
||||
|
||||
**Code Example (`pkg/mining/mining.go`):**
|
||||
\`\`\`go
|
||||
// In Config.Validate():
|
||||
// ...
|
||||
// Example for LogFile
|
||||
if c.LogFile != "" {
|
||||
// Basic validation: ensure it's just a filename, not a path
|
||||
if strings.Contains(c.LogFile, "/") || strings.Contains(c.LogFile, "\\") {
|
||||
return fmt.Errorf("LogFile cannot be a path")
|
||||
}
|
||||
// Use an allowlist for the filename itself
|
||||
allowedChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_."
|
||||
if !isValidInput(c.LogFile, allowedChars) {
|
||||
return fmt.Errorf("LogFile contains invalid characters")
|
||||
}
|
||||
}
|
||||
|
||||
// Example for CPUPriority
|
||||
if c.CPUPriority < 0 || c.CPUPriority > 5 {
|
||||
return fmt.Errorf("CPUPriority must be between 0 and 5")
|
||||
}
|
||||
// ...
|
||||
\`\`\`
|
||||
71
AUDIT-MEMORY.md
Normal file
71
AUDIT-MEMORY.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Memory and Resource Management Audit
|
||||
|
||||
This audit examines the application's memory and resource management based on a review of the codebase, with a focus on `pkg/mining/manager.go`, `pkg/mining/service.go`, and `pkg/database/database.go`.
|
||||
|
||||
## 1. Goroutine Leak Analysis
|
||||
|
||||
The application uses several long-running goroutines for background tasks. Overall, goroutine lifecycle management is robust, but there are minor areas for improvement.
|
||||
|
||||
### Findings:
|
||||
|
||||
- **Stats Collection (`manager.go`):** The `startStatsCollection` goroutine runs in a `for` loop with a `time.Ticker`. It reliably terminates when the `stopChan` is closed during `Manager.Stop()`.
|
||||
- **Database Cleanup (`manager.go`):** The `startDBCleanup` goroutine also uses a `time.Ticker` and correctly listens for the `stopChan` signal, ensuring it exits cleanly.
|
||||
- **WebSocket Event Hub (`service.go`):** The `EventHub.Run` method is launched as a goroutine and manages client connections. It terminates when its internal `quit` channel is closed, which is triggered by the `EventHub.Stop()` method.
|
||||
|
||||
### Recommendations:
|
||||
|
||||
- **No major issues found.** The use of `stopChan` and `sync.WaitGroup` in `Manager` provides a solid foundation for graceful shutdowns.
|
||||
|
||||
## 2. Memory Leak Analysis
|
||||
|
||||
The primary areas of concern for memory leaks are in-memory data structures that could grow indefinitely.
|
||||
|
||||
### Findings:
|
||||
|
||||
- **`Manager.miners` Map:** The `miners` map in the `Manager` struct stores active miner processes. Entries are added in `StartMiner` and removed in `StopMiner` and `UninstallMiner`. If a miner process were to crash or become unresponsive without `StopMiner` being called, its entry would persist in the map, causing a minor memory leak.
|
||||
- **In-Memory Hashrate History:** Each miner maintains an in-memory `HashrateHistory`. The `ReduceHashrateHistory` method is called periodically to trim this data, preventing unbounded growth. This is a good practice.
|
||||
- **Request Body Size Limit:** The `service.go` file correctly implements a 1MB request body size limit, which helps prevent memory exhaustion from large API requests.
|
||||
|
||||
### Recommendations:
|
||||
|
||||
- **Implement a health check for miners.** A periodic health check could detect unresponsive miner processes and trigger their removal from the `miners` map, preventing memory leaks from orphaned entries.
|
||||
|
||||
## 3. Database Resource Management
|
||||
|
||||
The application uses an SQLite database for persisting historical data.
|
||||
|
||||
### Findings:
|
||||
|
||||
- **Connection Pooling:** The `database.go` file configures the connection pool with `SetMaxOpenConns(1)`. This is appropriate for SQLite's single-writer model and prevents connection-related issues.
|
||||
- **`hashrate_history` Cleanup:** The `Cleanup` function in `database.go` correctly removes old records from the `hashrate_history` table based on the configured retention period.
|
||||
- **`miner_sessions` Table:** The `miner_sessions` table tracks miner uptime but has no corresponding cleanup mechanism. This table will grow indefinitely, leading to a gradual increase in database size and a potential performance degradation over time.
|
||||
|
||||
### Recommendations:
|
||||
|
||||
- **Add a cleanup mechanism for `miner_sessions`.** Extend the `Cleanup` function to also remove old records from the `miner_sessions` table based on the retention period.
|
||||
|
||||
## 4. File Handle and Process Management
|
||||
|
||||
The application manages external miner processes, which requires careful handling of file descriptors and process handles.
|
||||
|
||||
### Findings:
|
||||
|
||||
- **Process Lifecycle:** The `Stop` method on miner implementations (`xmrig.go`, `ttminer.go`) is responsible for terminating the `exec.Cmd` process. This appears to be handled correctly.
|
||||
- **I/O Pipes:** The miner's `stdout`, `stderr`, and `stdin` pipes are created and managed. The code does not show any obvious leaks of these file handles.
|
||||
|
||||
### Recommendations:
|
||||
|
||||
- **No major issues found.** The process management logic appears to be sound.
|
||||
|
||||
## 5. Network Connection Handling
|
||||
|
||||
The application's API server and WebSocket endpoint are critical areas for resource management.
|
||||
|
||||
### Findings:
|
||||
|
||||
- **HTTP Server Timeouts:** The `service.go` file correctly configures `ReadTimeout`, `WriteTimeout`, and `IdleTimeout` for the HTTP server, which is a best practice for preventing slow client attacks and connection exhaustion.
|
||||
- **WebSocket Connections:** The `wsUpgrader` has a `CheckOrigin` function that restricts connections to `localhost` origins, providing a layer of security. The `EventHub` manages the lifecycle of WebSocket connections.
|
||||
|
||||
### Recommendations:
|
||||
|
||||
- **No major issues found.** The network connection handling is well-configured.
|
||||
40
AUDIT-PERFORMANCE.md
Normal file
40
AUDIT-PERFORMANCE.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Performance Audit Report
|
||||
|
||||
This report details the findings of a performance audit conducted on the codebase. It covers several areas, including database performance, memory usage, concurrency, API performance, and build/deploy performance.
|
||||
|
||||
## Database Performance
|
||||
|
||||
The application uses SQLite with WAL (Write-Ahead Logging) enabled, which is a good choice for the application's needs, as it allows for concurrent reads and writes. The database schema is well-defined, and the indexes on the `hashrate_history` and `miner_sessions` tables are appropriate for the queries being performed.
|
||||
|
||||
- **N+1 Queries:** No evidence of N+1 queries was found. The database interactions are straightforward and do not involve complex object relational mapping.
|
||||
- **Missing Indexes:** The existing indexes are well-suited for the application's queries. No missing indexes were identified.
|
||||
- **Large Result Sets:** The history endpoints could potentially return large result sets. Implementing pagination would be a good proactive measure to prevent performance degradation as the data grows.
|
||||
- **Inefficient Joins:** The database schema is simple and does not involve complex joins. No inefficient joins were identified.
|
||||
- **Connection Pooling:** The connection pool is configured to use a single connection, which is appropriate for SQLite.
|
||||
|
||||
## Memory Usage
|
||||
|
||||
- **Memory Leaks:** No obvious memory leaks were identified. The application's memory usage appears to be stable.
|
||||
- **Large Object Loading:** The log and history endpoints could potentially load large amounts of data into memory. Implementing streaming for these endpoints would be a good way to mitigate this.
|
||||
- **Cache Efficiency:** The API uses a simple time-based cache for some endpoints, which is effective but could be improved. A more sophisticated caching mechanism, such as an LRU cache, could be used to improve cache efficiency.
|
||||
- **Garbage Collection:** No issues with garbage collection were identified.
|
||||
|
||||
## Concurrency
|
||||
|
||||
- **Blocking Operations:** The `CheckInstallation` function in `xmrig.go` shells out to the command line, which is a blocking operation. This could be optimized by using a different method to check for the miner's presence.
|
||||
- **Lock Contention:** The `Manager` uses a mutex to protect the `miners` map, which is good for preventing race conditions. However, the stats collection iterates over all miners and collects stats sequentially, which could be a bottleneck. This could be improved by collecting stats in parallel.
|
||||
- **Thread Pool Sizing:** The application does not use a thread pool.
|
||||
- **Async Opportunities:** The `build-all` target in the `Makefile` builds for multiple platforms sequentially. This could be parallelized to reduce build times. Similarly, the `before` hook in `.goreleaser.yaml` runs tests and UI builds sequentially, which could also be parallelized.
|
||||
|
||||
## API Performance
|
||||
|
||||
- **Response Times:** The API response times are generally good.
|
||||
- **Payload Sizes:** The log and history endpoints could potentially return large payloads. Implementing response compression would be a good way to reduce payload sizes.
|
||||
- **Caching Headers:** The API uses `Cache-Control` headers, which is good.
|
||||
- **Rate Limiting:** The API has rate limiting in place, which is good.
|
||||
|
||||
## Build/Deploy Performance
|
||||
|
||||
- **Build Time:** The `build-all` target in the `Makefile` builds for multiple platforms sequentially. This could be parallelized to reduce build times. The `before` hook in `.goreleaser.yaml` runs tests and UI builds sequentially, which could also be parallelized.
|
||||
- **Asset Size:** The UI assets are not minified or compressed, which could increase load times.
|
||||
- **Cold Start:** The application has a fast cold start time.
|
||||
72
AUDIT-PROTOCOL.md
Normal file
72
AUDIT-PROTOCOL.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Mining Protocol Security Audit: AUDIT-PROTOCOL.md
|
||||
|
||||
## 1. Stratum Protocol Security
|
||||
|
||||
**Findings:**
|
||||
|
||||
- **Insecure Default Connections:** The miner defaults to `stratum+tcp`, transmitting data in plaintext. This exposes sensitive information, such as wallet addresses and passwords, to interception. An attacker with network access could easily capture and exploit this data.
|
||||
|
||||
- **Lack of Certificate Pinning:** Although TLS is an option, there is no mechanism for certificate pinning. Without it, the client cannot verify the authenticity of the pool's certificate, leaving it vulnerable to man-in-the-middle attacks where a malicious actor could impersonate the mining pool.
|
||||
|
||||
- **Vulnerability to Protocol-Level Attacks:** The Stratum protocol implementation does not adequately protect against attacks like share hijacking or difficulty manipulation. An attacker could potentially modify Stratum messages to redirect shares or disrupt the mining process.
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
- **Enforce TLS by Default:** Mandate the use of `stratum+ssl` to ensure all communication between the miner and the pool is encrypted.
|
||||
- **Implement Certificate Pinning:** Add support for certificate pinning to allow users to specify the expected certificate, preventing man-in-the-middle attacks.
|
||||
- **Add Protocol-Level Integrity Checks:** Implement checksums or signatures for Stratum messages to ensure their integrity and prevent tampering.
|
||||
|
||||
## 2. Pool Authentication
|
||||
|
||||
**Findings:**
|
||||
|
||||
- **Credentials in Plaintext:** Authentication credentials, including the worker's username and password, are sent in plaintext over unencrypted connections. This makes them highly susceptible to theft.
|
||||
|
||||
- **Weak Password Hashing:** The `config.json` file stores the password as `"x"`, which is a weak default. While users can change this, there is no enforcement of strong password policies.
|
||||
|
||||
- **Risk of Brute-Force Attacks:** The absence of rate limiting or account lockout mechanisms on the pool side exposes the authentication process to brute-force attacks, where an attacker could repeatedly guess passwords until they gain access.
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
- **Mandate Encrypted Authentication:** Require all authentication attempts to be transmitted over a TLS-encrypted connection.
|
||||
- **Enforce Strong Password Policies:** Encourage the use of strong, unique passwords and consider implementing a password strength meter.
|
||||
- **Implement Secure Authentication Mechanisms:** Support more secure authentication methods, such as token-based authentication, to reduce the reliance on passwords.
|
||||
|
||||
## 3. Share Validation
|
||||
|
||||
**Findings:**
|
||||
|
||||
- **Lack of Share Signatures:** Shares submitted by the miner are not cryptographically signed, making it possible for an attacker to intercept and modify them. This could lead to share stealing, where an attacker redirects a legitimate miner's work to their own account.
|
||||
|
||||
- **Vulnerability to Replay Attacks:** There is no protection against replay attacks, where an attacker could resubmit old shares. While pools may have some defenses, the client-side implementation lacks measures to prevent this.
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
- **Implement Share Signing:** Introduce a mechanism for miners to sign each share with a unique key, allowing the pool to verify its authenticity.
|
||||
- **Add Nonces to Shares:** Include a unique, single-use nonce in each share submission to prevent replay attacks.
|
||||
|
||||
## 4. Block Template Handling
|
||||
|
||||
**Findings:**
|
||||
|
||||
- **Centralized Block Template Distribution:** The miner relies on a centralized pool for block templates, creating a single point of failure. If the pool is compromised, an attacker could distribute malicious or inefficient templates.
|
||||
|
||||
- **No Template Validation:** The miner does not independently validate the block templates received from the pool. This makes it vulnerable to block withholding attacks, where a malicious pool sends invalid templates, causing the miner to waste resources on unsolvable blocks.
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
- **Support Decentralized Template Distribution:** Explore decentralized alternatives for block template distribution to reduce reliance on a single pool.
|
||||
- **Implement Independent Template Validation:** Add a mechanism for the miner to validate block templates against the network's consensus rules before starting to mine.
|
||||
|
||||
## 5. Network Message Validation
|
||||
|
||||
**Findings:**
|
||||
|
||||
- **Insufficient Input Sanitization:** Network messages from the pool are not consistently sanitized, creating a risk of denial-of-service attacks. An attacker could send malformed messages to crash the miner.
|
||||
|
||||
- **Lack of Rate Limiting:** The client does not implement rate limiting for incoming messages, making it vulnerable to flooding attacks that could overwhelm its resources.
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
- **Implement Robust Message Sanitization:** Sanitize all incoming network messages to ensure they conform to the expected format and do not contain malicious payloads.
|
||||
- **Add Rate Limiting:** Introduce rate limiting for incoming messages to prevent a single source from overwhelming the miner.
|
||||
44
AUDIT-SECRETS.md
Normal file
44
AUDIT-SECRETS.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Security Audit: Secrets & Configuration
|
||||
|
||||
This document outlines the findings of a security audit focused on exposed secrets and insecure configurations.
|
||||
|
||||
## 1. Secret Detection
|
||||
|
||||
### 1.1. Hardcoded Credentials & Sensitive Information
|
||||
|
||||
- **Placeholder Wallet Addresses:**
|
||||
- `miner/core/src/config.json`: Contains the placeholder `"YOUR_WALLET_ADDRESS"`.
|
||||
- `miner/proxy/src/config.json`: Contains the placeholder `"YOUR_WALLET"`.
|
||||
- `miner/core/doc/api/1/config.json`: Contains a hardcoded wallet address.
|
||||
|
||||
- **Default Passwords:**
|
||||
- `miner/core/src/config.json`: The `"pass"` field is set to `"x"`.
|
||||
- `miner/proxy/src/config.json`: The `"pass"` field is set to `"x"`.
|
||||
- `miner/core/doc/api/1/config.json`: The `"pass"` field is set to `"x"`.
|
||||
|
||||
- **Placeholder API Tokens:**
|
||||
- `miner/core/doc/api/1/config.json`: The `"access-token"` is set to the placeholder `"TOKEN"`.
|
||||
|
||||
## 2. Configuration Security
|
||||
|
||||
### 2.1. Insecure Default Configurations
|
||||
|
||||
- **`null` API Access Tokens:**
|
||||
- `miner/core/src/config.json`: The `http.access-token` is `null` by default. If the HTTP API is enabled without setting a token, it could allow unauthorized access.
|
||||
- `miner/proxy/src/config.json`: The `http.access-token` is `null` by default, posing a similar risk.
|
||||
|
||||
- **TLS Disabled by Default:**
|
||||
- `miner/core/src/config.json`: The `tls.enabled` flag is `false` by default. If services are exposed, communication would be unencrypted.
|
||||
- `miner/proxy/src/config.json`: While `tls.enabled` is `true`, the `cert` and `cert_key` fields are `null`, preventing a secure TLS connection from being established.
|
||||
|
||||
### 2.2. Verbose Error Messages
|
||||
|
||||
No instances of overly verbose error messages leaking sensitive information were identified during this audit.
|
||||
|
||||
### 2.3. CORS Policy
|
||||
|
||||
The CORS policy could not be audited as it was not explicitly defined in the scanned files.
|
||||
|
||||
### 2.4. Security Headers
|
||||
|
||||
No security headers (e.g., CSP, HSTS) were identified in the configuration files.
|
||||
127
AUDIT-TESTING.md
Normal file
127
AUDIT-TESTING.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Test Coverage and Quality Audit
|
||||
|
||||
## 1. Coverage Analysis
|
||||
|
||||
### Line Coverage
|
||||
|
||||
- **Overall Line Coverage: 44.4%**
|
||||
|
||||
The overall test coverage for the project is **44.4%**, which is below the recommended minimum of 80%. This indicates that a significant portion of the codebase is not covered by automated tests, increasing the risk of undetected bugs.
|
||||
|
||||
### Untested Code
|
||||
|
||||
The following files and functions have **0% test coverage** and should be prioritized for testing:
|
||||
|
||||
- **`pkg/node/controller.go`**
|
||||
- `NewController`
|
||||
- `handleResponse`
|
||||
- `sendRequest`
|
||||
- `GetRemoteStats`
|
||||
- `StartRemoteMiner`
|
||||
- `StopRemoteMiner`
|
||||
- `GetRemoteLogs`
|
||||
- `GetAllStats`
|
||||
- `PingPeer`
|
||||
- `ConnectToPeer`
|
||||
- `DisconnectFromPeer`
|
||||
|
||||
- **`pkg/node/transport.go`**
|
||||
- `IsDuplicate`
|
||||
- `Mark`
|
||||
- `Cleanup`
|
||||
- `NewPeerRateLimiter`
|
||||
- `Allow`
|
||||
- `Start`
|
||||
- `Stop`
|
||||
- `OnMessage`
|
||||
- `Connect`
|
||||
- `Send`
|
||||
- `Broadcast`
|
||||
- `GetConnection`
|
||||
- `handleWSUpgrade`
|
||||
- `performHandshake`
|
||||
- `readLoop`
|
||||
- `keepalive`
|
||||
- `removeConnection`
|
||||
- `Close`
|
||||
- `GracefulClose`
|
||||
- `encryptMessage`
|
||||
- `decryptMessage`
|
||||
- `ConnectedPeers`
|
||||
|
||||
- **`pkg/mining/xmrig.go`**
|
||||
- `Uninstall`
|
||||
|
||||
- **`pkg/node/dispatcher.go`**
|
||||
- `DispatchUEPS`
|
||||
|
||||
- **`pkg/node/identity.go`**
|
||||
- `handleHandshake`
|
||||
- `handleComputeRequest`
|
||||
- `enterRehabMode`
|
||||
- `handleApplicationData`
|
||||
|
||||
## 2. Test Quality
|
||||
|
||||
### Test Independence
|
||||
|
||||
The existing tests appear to be isolated and do not share mutable state. However, the lack of comprehensive integration tests means that the interactions between components are not well-tested.
|
||||
|
||||
### Test Clarity
|
||||
|
||||
The test names are generally descriptive, but they could be improved by following a more consistent naming convention. The Arrange-Act-Assert pattern is not consistently applied, which can make the tests harder to understand.
|
||||
|
||||
### Test Reliability
|
||||
|
||||
The tests are not flaky and do not have any time-dependent failures. However, the lack of mocking for external dependencies means that the tests are not as reliable as they could be.
|
||||
|
||||
## 3. Missing Tests
|
||||
|
||||
### Edge Cases
|
||||
|
||||
The tests do not cover a sufficient number of edge cases, such as null inputs, empty strings, and boundary values.
|
||||
|
||||
### Error Paths
|
||||
|
||||
The tests do not adequately cover error paths, which can lead to unhandled exceptions in production.
|
||||
|
||||
### Security Tests
|
||||
|
||||
There are no security tests to check for vulnerabilities such as authentication bypass or injection attacks.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
The lack of integration tests means that the interactions between different components are not well-tested.
|
||||
|
||||
## 4. Suggested Tests to Add
|
||||
|
||||
### `pkg/node/controller.go`
|
||||
|
||||
- `TestNewController`: Verify that a new controller is created with the correct initial state.
|
||||
- `TestHandleResponse`: Test that the controller correctly handles incoming responses.
|
||||
- `TestSendRequest`: Test that the controller can send requests and receive responses.
|
||||
- `TestGetRemoteStats`: Test that the controller can retrieve stats from a remote peer.
|
||||
- `TestStartRemoteMiner`: Test that the controller can start a miner on a remote peer.
|
||||
- `TestStopRemoteMiner`: Test that the controller can stop a miner on a remote peer.
|
||||
- `TestGetRemoteLogs`: Test that the controller can retrieve logs from a remote peer.
|
||||
- `TestGetAllStats`: Test that the controller can retrieve stats from all connected peers.
|
||||
- `TestPingPeer`: Test that the controller can ping a remote peer.
|
||||
- `TestConnectToPeer`: Test that the controller can connect to a remote peer.
|
||||
- `TestDisconnectFromPeer`: Test that the controller can disconnect from a remote peer.
|
||||
|
||||
### `pkg/node/transport.go`
|
||||
|
||||
- `TestTransportStartAndStop`: Test that the transport can be started and stopped correctly.
|
||||
- `TestTransportConnect`: Test that the transport can connect to a remote peer.
|
||||
- `TestTransportSendAndReceive`: Test that the transport can send and receive messages.
|
||||
- `TestTransportBroadcast`: Test that the transport can broadcast messages to all connected peers.
|
||||
- `TestTransportHandshake`: Test that the transport correctly performs the handshake with a remote peer.
|
||||
- `TestTransportEncryption`: Test that the transport correctly encrypts and decrypts messages.
|
||||
|
||||
### `pkg/mining/xmrig.go`
|
||||
|
||||
- `TestUninstall`: Test that the `Uninstall` function correctly removes the miner binary.
|
||||
|
||||
### `pkg/node/dispatcher.go`
|
||||
|
||||
- `TestDispatchUEPS`: Test that the `DispatchUEPS` function correctly dispatches incoming packets.
|
||||
95
Makefile
95
Makefile
|
|
@ -1,4 +1,4 @@
|
|||
.PHONY: all build test clean install run demo help lint fmt vet docs install-swag dev package e2e e2e-ui e2e-api test-cpp test-cpp-core test-cpp-proxy build-cpp-tests
|
||||
.PHONY: all build test clean install run demo help lint fmt vet docs install-swag dev package e2e e2e-ui e2e-api test-cpp test-cpp-core test-cpp-proxy build-cpp-tests build-miner build-miner-core build-miner-proxy build-miner-all
|
||||
|
||||
# Variables
|
||||
BINARY_NAME=miner-ctrl
|
||||
|
|
@ -66,6 +66,34 @@ build-cpp-tests-proxy:
|
|||
$(CMAKE) -DBUILD_TESTS=ON .. && \
|
||||
$(CMAKE) --build . --target unit_tests integration_tests --parallel
|
||||
|
||||
# Build miner binaries (release builds)
|
||||
build-miner: build-miner-core build-miner-proxy
|
||||
@echo "Miner binaries built successfully"
|
||||
|
||||
# Build miner core (CPU/GPU miner)
|
||||
build-miner-core:
|
||||
@echo "Building miner core..."
|
||||
@mkdir -p $(MINER_CORE_BUILD_DIR)
|
||||
@cd $(MINER_CORE_BUILD_DIR) && \
|
||||
$(CMAKE) -DCMAKE_BUILD_TYPE=Release .. && \
|
||||
$(CMAKE) --build . --config Release --parallel
|
||||
|
||||
# Build miner proxy
|
||||
build-miner-proxy:
|
||||
@echo "Building miner proxy..."
|
||||
@mkdir -p $(MINER_PROXY_BUILD_DIR)
|
||||
@cd $(MINER_PROXY_BUILD_DIR) && \
|
||||
$(CMAKE) -DCMAKE_BUILD_TYPE=Release .. && \
|
||||
$(CMAKE) --build . --config Release --parallel
|
||||
|
||||
# Build all miner components and package
|
||||
build-miner-all: build-miner
|
||||
@echo "Packaging miner binaries..."
|
||||
@mkdir -p dist/miner
|
||||
@cp $(MINER_CORE_BUILD_DIR)/miner dist/miner/ 2>/dev/null || true
|
||||
@cp $(MINER_PROXY_BUILD_DIR)/miner-proxy dist/miner/ 2>/dev/null || true
|
||||
@echo "Miner binaries available in dist/miner/"
|
||||
|
||||
# Run C++ tests (builds first if needed)
|
||||
test-cpp: test-cpp-proxy
|
||||
@echo "All C++ tests completed"
|
||||
|
|
@ -177,30 +205,41 @@ e2e-api: build
|
|||
# Help
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " all - Run tests and build"
|
||||
@echo " build - Build the CLI binary"
|
||||
@echo " build-all - Build for multiple platforms"
|
||||
@echo " install - Install the binary"
|
||||
@echo " test - Run all tests (Go + C++)"
|
||||
@echo " test-go - Run Go tests only"
|
||||
@echo " test-cpp - Run C++ tests (core + proxy)"
|
||||
@echo " test-cpp-core - Run miner/core C++ tests"
|
||||
@echo " test-cpp-proxy - Run miner/proxy C++ tests"
|
||||
@echo " build-cpp-tests - Build all C++ tests"
|
||||
@echo " coverage - Run tests with coverage report"
|
||||
@echo " demo - Run the demo"
|
||||
@echo " run - Build and run the CLI"
|
||||
@echo " clean - Clean build artifacts (including C++ builds)"
|
||||
@echo " fmt - Format code"
|
||||
@echo " vet - Run go vet"
|
||||
@echo " lint - Run linters"
|
||||
@echo " tidy - Tidy dependencies"
|
||||
@echo " deps - Download dependencies"
|
||||
@echo " docs - Generate Swagger documentation"
|
||||
@echo " install-swag- Install the swag CLI"
|
||||
@echo " package - Create local distribution packages using GoReleaser"
|
||||
@echo " dev - Start the development server with docs and build"
|
||||
@echo " e2e - Run E2E tests with Playwright"
|
||||
@echo " e2e-ui - Open Playwright UI for interactive testing"
|
||||
@echo " e2e-api - Run API-only E2E tests"
|
||||
@echo " help - Show this help message"
|
||||
@echo ""
|
||||
@echo "Go Application:"
|
||||
@echo " all - Run tests and build"
|
||||
@echo " build - Build the CLI binary"
|
||||
@echo " build-all - Build for multiple platforms"
|
||||
@echo " install - Install the binary"
|
||||
@echo " run - Build and run the CLI"
|
||||
@echo " dev - Start the development server with docs and build"
|
||||
@echo ""
|
||||
@echo "Miner (C++ Binaries):"
|
||||
@echo " build-miner - Build miner core and proxy"
|
||||
@echo " build-miner-core - Build miner core only"
|
||||
@echo " build-miner-proxy - Build miner proxy only"
|
||||
@echo " build-miner-all - Build and package all miner binaries"
|
||||
@echo ""
|
||||
@echo "Testing:"
|
||||
@echo " test - Run all tests (Go + C++)"
|
||||
@echo " test-go - Run Go tests only"
|
||||
@echo " test-cpp - Run C++ tests (proxy)"
|
||||
@echo " test-cpp-core - Run miner/core C++ tests"
|
||||
@echo " test-cpp-proxy- Run miner/proxy C++ tests"
|
||||
@echo " coverage - Run tests with coverage report"
|
||||
@echo " e2e - Run E2E tests with Playwright"
|
||||
@echo " e2e-ui - Open Playwright UI for interactive testing"
|
||||
@echo " e2e-api - Run API-only E2E tests"
|
||||
@echo ""
|
||||
@echo "Code Quality:"
|
||||
@echo " fmt - Format code"
|
||||
@echo " vet - Run go vet"
|
||||
@echo " lint - Run linters"
|
||||
@echo " tidy - Tidy dependencies"
|
||||
@echo ""
|
||||
@echo "Other:"
|
||||
@echo " clean - Clean all build artifacts"
|
||||
@echo " deps - Download dependencies"
|
||||
@echo " docs - Generate Swagger documentation"
|
||||
@echo " package - Create local distribution packages"
|
||||
@echo " help - Show this help message"
|
||||
|
|
|
|||
48
README.md
48
README.md
|
|
@ -108,25 +108,43 @@ wails3 build
|
|||
```
|
||||
Mining/
|
||||
├── cmd/
|
||||
│ ├── mining/ # CLI application
|
||||
│ ├── mining/ # CLI application (miner-ctrl)
|
||||
│ └── desktop/ # Wails desktop app
|
||||
├── pkg/mining/ # Core Go package
|
||||
│ ├── mining.go # Interfaces and types
|
||||
│ ├── manager.go # Miner lifecycle management
|
||||
│ ├── service.go # RESTful API (Gin)
|
||||
│ ├── xmrig.go # XMRig implementation
|
||||
│ └── profile_manager.go # Profile persistence
|
||||
├── miner/core/ # Modified XMRig with algorithm support
|
||||
│ └── src/
|
||||
│ ├── backend/opencl/ # OpenCL GPU kernels
|
||||
│ ├── backend/cuda/ # CUDA GPU kernels
|
||||
│ └── crypto/ # Algorithm implementations
|
||||
├── miner/ # Standalone C++ mining tools
|
||||
│ ├── core/ # CPU/GPU miner binary
|
||||
│ ├── proxy/ # Stratum proxy for farms
|
||||
│ ├── cuda/ # CUDA plugin for NVIDIA
|
||||
│ └── README.md # Miner documentation
|
||||
└── ui/ # Angular 20+ web dashboard
|
||||
└── src/app/
|
||||
├── components/ # Reusable UI components
|
||||
└── pages/ # Route pages
|
||||
```
|
||||
|
||||
## Standalone Miner Tools
|
||||
|
||||
The `miner/` directory contains standalone C++ mining programs that can be used independently without the GUI:
|
||||
|
||||
```bash
|
||||
# Build miner binaries
|
||||
make build-miner
|
||||
|
||||
# Or build individually
|
||||
make build-miner-core # CPU/GPU miner
|
||||
make build-miner-proxy # Stratum proxy
|
||||
|
||||
# Run directly
|
||||
./miner/core/build/miner -o pool.example.com:3333 -u WALLET -p x
|
||||
./miner/proxy/build/miner-proxy -o pool.example.com:3333 -b 0.0.0.0:3333
|
||||
```
|
||||
|
||||
Pre-built binaries are available from [Releases](https://github.com/letheanVPN/Mining/releases). See [miner/README.md](miner/README.md) for full documentation.
|
||||
|
||||
## API Reference
|
||||
|
||||
Base path: `/api/v1/mining`
|
||||
|
|
@ -158,26 +176,24 @@ Swagger UI: `http://localhost:9090/api/v1/mining/swagger/index.html`
|
|||
### Build Commands
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
# Go Backend
|
||||
make build # Build CLI binary
|
||||
make test # Run tests with coverage
|
||||
make test # Run all tests (Go + C++)
|
||||
make dev # Start dev server on :9090
|
||||
|
||||
# Miner (C++ Binaries)
|
||||
make build-miner # Build miner and proxy
|
||||
make build-miner-all # Build and package to dist/miner/
|
||||
|
||||
# Frontend
|
||||
cd ui
|
||||
npm install
|
||||
npm run build # Build web component
|
||||
npm test # Run unit tests (36 specs)
|
||||
npm test # Run unit tests
|
||||
|
||||
# Desktop
|
||||
cd cmd/desktop/mining-desktop
|
||||
wails3 build # Build native app
|
||||
|
||||
# Miner Core (GPU support)
|
||||
cd miner/core
|
||||
mkdir build && cd build
|
||||
cmake .. -DWITH_OPENCL=ON -DWITH_CUDA=ON
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
|
|
|||
1747
cmd/desktop/mining-desktop/frontend/package-lock.json
generated
1747
cmd/desktop/mining-desktop/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -26,7 +26,7 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^20.3.0",
|
||||
"@angular/compiler": "^20.3.0",
|
||||
"@angular/compiler": "^20.3.16",
|
||||
"@angular/core": "^20.3.0",
|
||||
"@angular/forms": "^20.3.0",
|
||||
"@angular/platform-browser": "^20.3.0",
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^20.3.6",
|
||||
"@angular/cli": "^20.3.6",
|
||||
"@angular/cli": "^20.3.13",
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ require (
|
|||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.57.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/samber/lo v1.49.1 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
|
|
@ -91,10 +91,9 @@ require (
|
|||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.20.0 // indirect
|
||||
golang.org/x/crypto v0.44.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
|
|
|
|||
|
|
@ -167,10 +167,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.57.0 h1:AsSSrrMs4qI/hLrKlTH/TGQeTMY0ib1pAOX7vA3AdqE=
|
||||
github.com/quic-go/quic-go v0.57.0/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
|
|
@ -221,8 +221,8 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI
|
|||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||
|
|
@ -230,8 +230,8 @@ golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
|
|
@ -278,6 +278,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
|
|
|||
11
go.mod
11
go.mod
|
|
@ -7,6 +7,7 @@ require (
|
|||
github.com/Snider/Borg v0.0.2
|
||||
github.com/Snider/Poindexter v0.0.0-20251229183216-e182d4f49741
|
||||
github.com/adrg/xdg v0.5.3
|
||||
github.com/ckanthony/gin-mcp v0.0.0-20251107113615-3c631c4fa9f4
|
||||
github.com/gin-contrib/cors v1.7.6
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/google/uuid v1.6.0
|
||||
|
|
@ -17,6 +18,7 @@ require (
|
|||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/swaggo/swag v1.16.6
|
||||
golang.org/x/text v0.31.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -25,7 +27,6 @@ require (
|
|||
github.com/Snider/Enchantrix v0.0.2 // indirect
|
||||
github.com/bytedance/sonic v1.14.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/ckanthony/gin-mcp v0.0.0-20251107113615-3c631c4fa9f4 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/ebitengine/purego v0.9.0 // indirect
|
||||
|
|
@ -57,8 +58,8 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.57.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
|
|
@ -67,15 +68,13 @@ require (
|
|||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.20.0 // indirect
|
||||
golang.org/x/crypto v0.44.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
)
|
||||
|
|
|
|||
18
go.sum
18
go.sum
|
|
@ -113,10 +113,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.57.0 h1:AsSSrrMs4qI/hLrKlTH/TGQeTMY0ib1pAOX7vA3AdqE=
|
||||
github.com/quic-go/quic-go v0.57.0/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
|
|
@ -155,16 +155,16 @@ github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2W
|
|||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
|
|
@ -200,6 +200,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
|
|
|||
289
miner/README.md
Normal file
289
miner/README.md
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
# Lethean Miner Suite
|
||||
|
||||
[](https://opensource.org/license/eupl-1-2)
|
||||
[](https://github.com/letheanVPN/Mining/releases)
|
||||
|
||||
High-performance cryptocurrency mining tools. These standalone C++ programs can be used independently or managed through the Mining Platform GUI.
|
||||
|
||||
## Components
|
||||
|
||||
| Component | Description | Binary |
|
||||
|-----------|-------------|--------|
|
||||
| [**core**](core/) | CPU/GPU miner with full algorithm support | `miner` |
|
||||
| [**proxy**](proxy/) | Stratum proxy for mining farms (100K+ connections) | `miner-proxy` |
|
||||
| [**cuda**](cuda/) | CUDA plugin for NVIDIA GPUs | `libminer-cuda.so` |
|
||||
| [**config**](config/) | Configuration generator tool | `miner-config` |
|
||||
| [**workers**](workers/) | Worker management utilities | `miner-workers` |
|
||||
| [**heatmap**](heatmap/) | Hardware temperature visualization | `miner-heatmap` |
|
||||
|
||||
## Supported Algorithms
|
||||
|
||||
### CPU Mining
|
||||
| Algorithm | Coins |
|
||||
|-----------|-------|
|
||||
| RandomX | Monero (XMR), Lethean (LTHN), Wownero (WOW) |
|
||||
| CryptoNight | Various CN variants |
|
||||
| GhostRider | Raptoreum (RTM) |
|
||||
| Argon2 | Chukwa, Ninja |
|
||||
|
||||
### GPU Mining (OpenCL/CUDA)
|
||||
| Algorithm | Coins |
|
||||
|-----------|-------|
|
||||
| RandomX | Monero, Lethean |
|
||||
| KawPow | Ravencoin (RVN), Neoxa |
|
||||
| ETChash | Ethereum Classic (ETC) |
|
||||
| ProgPowZ | Zano (ZANO) |
|
||||
| Blake3 | Decred (DCR) |
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Download Pre-built Binaries
|
||||
|
||||
Download from [Releases](https://github.com/letheanVPN/Mining/releases):
|
||||
- `miner-linux-x64.tar.gz` - Linux x86_64
|
||||
- `miner-linux-arm64.tar.gz` - Linux ARM64
|
||||
- `miner-macos-x64.tar.gz` - macOS Intel
|
||||
- `miner-macos-arm64.tar.gz` - macOS Apple Silicon
|
||||
- `miner-windows-x64.zip` - Windows x64
|
||||
|
||||
### Run the Miner
|
||||
|
||||
```bash
|
||||
# Basic CPU mining
|
||||
./miner -o pool.example.com:3333 -u YOUR_WALLET -p x
|
||||
|
||||
# With config file (recommended)
|
||||
./miner -c config.json
|
||||
|
||||
# CPU + GPU mining
|
||||
./miner -c config.json --opencl --cuda
|
||||
|
||||
# Show help
|
||||
./miner --help
|
||||
```
|
||||
|
||||
### Run the Proxy
|
||||
|
||||
```bash
|
||||
# Start proxy for mining farm
|
||||
./miner-proxy -o pool.example.com:3333 -u YOUR_WALLET -b 0.0.0.0:3333
|
||||
|
||||
# With config file
|
||||
./miner-proxy -c proxy-config.json
|
||||
```
|
||||
|
||||
## Building from Source
|
||||
|
||||
### Prerequisites
|
||||
|
||||
**All Platforms:**
|
||||
- CMake 3.10+
|
||||
- C++11 compatible compiler
|
||||
- libuv
|
||||
- OpenSSL (for TLS support)
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
sudo apt-get install build-essential cmake libuv1-dev libssl-dev libhwloc-dev
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install cmake libuv openssl hwloc
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
- Visual Studio 2019+ with C++ workload
|
||||
- vcpkg for dependencies
|
||||
|
||||
### Build Miner Core
|
||||
|
||||
```bash
|
||||
cd core
|
||||
mkdir build && cd build
|
||||
|
||||
# Standard build
|
||||
cmake ..
|
||||
cmake --build . --config Release -j$(nproc)
|
||||
|
||||
# With GPU support
|
||||
cmake .. -DWITH_OPENCL=ON -DWITH_CUDA=ON
|
||||
|
||||
# Static build (portable)
|
||||
cmake .. -DBUILD_STATIC=ON
|
||||
|
||||
# Minimal build (RandomX only)
|
||||
cmake .. -DWITH_ARGON2=OFF -DWITH_KAWPOW=OFF -DWITH_GHOSTRIDER=OFF
|
||||
```
|
||||
|
||||
### Build Proxy
|
||||
|
||||
```bash
|
||||
cd proxy
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release -j$(nproc)
|
||||
```
|
||||
|
||||
### Build All Components
|
||||
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
make build-miner # Build miner core
|
||||
make build-miner-proxy # Build proxy
|
||||
make build-miner-all # Build all components
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Miner Config (config.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"autosave": true,
|
||||
"cpu": true,
|
||||
"opencl": false,
|
||||
"cuda": false,
|
||||
"pools": [
|
||||
{
|
||||
"url": "stratum+tcp://pool.example.com:3333",
|
||||
"user": "YOUR_WALLET",
|
||||
"pass": "x",
|
||||
"keepalive": true,
|
||||
"tls": false
|
||||
}
|
||||
],
|
||||
"http": {
|
||||
"enabled": true,
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080,
|
||||
"access-token": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Proxy Config (proxy-config.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "nicehash",
|
||||
"pools": [
|
||||
{
|
||||
"url": "stratum+tcp://pool.example.com:3333",
|
||||
"user": "YOUR_WALLET",
|
||||
"pass": "x"
|
||||
}
|
||||
],
|
||||
"bind": [
|
||||
{
|
||||
"host": "0.0.0.0",
|
||||
"port": 3333
|
||||
}
|
||||
],
|
||||
"http": {
|
||||
"enabled": true,
|
||||
"host": "127.0.0.1",
|
||||
"port": 8081
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## HTTP API
|
||||
|
||||
Both miner and proxy expose HTTP APIs for monitoring and control.
|
||||
|
||||
### Miner API (default: http://127.0.0.1:8080)
|
||||
|
||||
| Endpoint | Description |
|
||||
|----------|-------------|
|
||||
| `GET /1/summary` | Mining statistics |
|
||||
| `GET /1/threads` | Per-thread hashrates |
|
||||
| `GET /1/config` | Current configuration |
|
||||
| `PUT /1/config` | Update configuration |
|
||||
|
||||
### Proxy API (default: http://127.0.0.1:8081)
|
||||
|
||||
| Endpoint | Description |
|
||||
|----------|-------------|
|
||||
| `GET /1/summary` | Proxy statistics |
|
||||
| `GET /1/workers` | Connected workers |
|
||||
| `GET /1/config` | Current configuration |
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
### CPU Mining
|
||||
|
||||
```bash
|
||||
# Enable huge pages (Linux)
|
||||
sudo sysctl -w vm.nr_hugepages=1280
|
||||
|
||||
# Or permanent (add to /etc/sysctl.conf)
|
||||
echo "vm.nr_hugepages=1280" | sudo tee -a /etc/sysctl.conf
|
||||
|
||||
# Enable 1GB pages (better performance)
|
||||
sudo ./scripts/enable_1gb_pages.sh
|
||||
```
|
||||
|
||||
### GPU Mining
|
||||
|
||||
```bash
|
||||
# AMD GPUs - increase virtual memory
|
||||
# Add to /etc/security/limits.conf:
|
||||
# * soft memlock unlimited
|
||||
# * hard memlock unlimited
|
||||
|
||||
# NVIDIA GPUs - optimize power
|
||||
nvidia-smi -pl 120 # Set power limit
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Run miner tests
|
||||
cd core/build
|
||||
ctest --output-on-failure
|
||||
|
||||
# Run proxy tests
|
||||
cd proxy/build
|
||||
./tests/unit_tests
|
||||
./tests/integration_tests
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
miner/
|
||||
├── core/ # Main miner (CPU/GPU)
|
||||
│ ├── src/
|
||||
│ │ ├── backend/ # CPU, OpenCL, CUDA backends
|
||||
│ │ ├── crypto/ # Algorithm implementations
|
||||
│ │ ├── base/ # Network, I/O, utilities
|
||||
│ │ └── core/ # Configuration, controller
|
||||
│ ├── scripts/ # Build and setup scripts
|
||||
│ └── CMakeLists.txt
|
||||
├── proxy/ # Stratum proxy
|
||||
│ ├── src/
|
||||
│ │ ├── proxy/ # Proxy core (splitters, events)
|
||||
│ │ └── base/ # Shared base code
|
||||
│ ├── tests/ # Unit and integration tests
|
||||
│ └── CMakeLists.txt
|
||||
├── cuda/ # CUDA plugin
|
||||
├── config/ # Config generator
|
||||
├── workers/ # Worker utilities
|
||||
├── heatmap/ # Temperature visualization
|
||||
├── deps/ # Dependency build scripts
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2025 Lethean <https://lethean.io>
|
||||
|
||||
Licensed under the European Union Public License 1.2 (EUPL-1.2).
|
||||
See [LICENSE](../LICENSE) for details.
|
||||
|
||||
## Related Projects
|
||||
|
||||
- [Mining Platform](../) - GUI management platform
|
||||
- [Lethean](https://lethean.io) - Lethean Network
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
"history": "4.7.2",
|
||||
"html-webpack-plugin": "3.1.0",
|
||||
"immutability-helper": "2.6.6",
|
||||
"lodash": "4.17.15",
|
||||
"lodash": "4.17.23",
|
||||
"random-id": "0.0.2",
|
||||
"react": "16.2.0",
|
||||
"react-autosize-textarea": "3.0.2",
|
||||
|
|
|
|||
|
|
@ -1,38 +1,192 @@
|
|||
# XMRig
|
||||
# Miner
|
||||
|
||||
[](https://github.com/xmrig/xmrig/releases)
|
||||
[](https://github.com/xmrig/xmrig/releases)
|
||||
[](https://github.com/xmrig/xmrig/releases)
|
||||
[](https://github.com/xmrig/xmrig/blob/master/LICENSE)
|
||||
[](https://github.com/xmrig/xmrig/stargazers)
|
||||
[](https://github.com/xmrig/xmrig/network)
|
||||
High-performance, cross-platform CPU/GPU cryptocurrency miner supporting RandomX, KawPow, CryptoNight, GhostRider, ETChash, ProgPowZ, and Blake3 algorithms.
|
||||
|
||||
XMRig is a high performance, open source, cross platform RandomX, KawPow, CryptoNight and [GhostRider](https://github.com/xmrig/xmrig/tree/master/src/crypto/ghostrider#readme) unified CPU/GPU miner and [RandomX benchmark](https://xmrig.com/benchmark). Official binaries are available for Windows, Linux, macOS and FreeBSD.
|
||||
## Features
|
||||
|
||||
## Mining backends
|
||||
### Mining Backends
|
||||
- **CPU** (x86/x64/ARMv7/ARMv8/RISC-V)
|
||||
- **OpenCL** for AMD GPUs.
|
||||
- **CUDA** for NVIDIA GPUs via external [CUDA plugin](https://github.com/xmrig/xmrig-cuda).
|
||||
- **OpenCL** for AMD GPUs
|
||||
- **CUDA** for NVIDIA GPUs via external [CUDA plugin](../cuda/)
|
||||
|
||||
## Download
|
||||
* **[Binary releases](https://github.com/xmrig/xmrig/releases)**
|
||||
* **[Build from source](https://xmrig.com/docs/miner/build)**
|
||||
### Supported Algorithms
|
||||
|
||||
## Usage
|
||||
The preferred way to configure the miner is the [JSON config file](https://xmrig.com/docs/miner/config) as it is more flexible and human friendly. The [command line interface](https://xmrig.com/docs/miner/command-line-options) does not cover all features, such as mining profiles for different algorithms. Important options can be changed during runtime without miner restart by editing the config file or executing [API](https://xmrig.com/docs/miner/api) calls.
|
||||
| Algorithm | Variants | CPU | GPU |
|
||||
|-----------|----------|-----|-----|
|
||||
| RandomX | rx/0, rx/wow, rx/arq, rx/graft, rx/sfx, rx/keva | Yes | Yes |
|
||||
| CryptoNight | cn/0-2, cn-lite, cn-heavy, cn-pico | Yes | Yes |
|
||||
| GhostRider | gr | Yes | No |
|
||||
| Argon2 | chukwa, chukwa2, ninja | Yes | No |
|
||||
| KawPow | kawpow | No | Yes |
|
||||
| ETChash | etchash, ethash | No | Yes |
|
||||
| ProgPowZ | progpowz | No | Yes |
|
||||
| Blake3 | blake3 | Yes | Yes |
|
||||
|
||||
* **[Wizard](https://xmrig.com/wizard)** helps you create initial configuration for the miner.
|
||||
* **[Workers](http://workers.xmrig.info)** helps manage your miners via HTTP API.
|
||||
## Quick Start
|
||||
|
||||
## Donations
|
||||
* Default donation 1% (1 minute in 100 minutes) can be increased via option `donate-level` or disabled in source code.
|
||||
* XMR: `48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD`
|
||||
### Download
|
||||
|
||||
## Developers
|
||||
* **[xmrig](https://github.com/xmrig)**
|
||||
* **[sech1](https://github.com/SChernykh)**
|
||||
Pre-built binaries are available from [Releases](https://github.com/letheanVPN/Mining/releases).
|
||||
|
||||
## Contacts
|
||||
* support@xmrig.com
|
||||
* [reddit](https://www.reddit.com/user/XMRig/)
|
||||
* [twitter](https://twitter.com/xmrig_dev)
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# Basic CPU mining to a pool
|
||||
./miner -o pool.example.com:3333 -u YOUR_WALLET -p x
|
||||
|
||||
# With JSON config (recommended)
|
||||
./miner -c config.json
|
||||
|
||||
# Enable GPU mining
|
||||
./miner -c config.json --opencl --cuda
|
||||
|
||||
# Benchmark mode
|
||||
./miner --bench=1M
|
||||
|
||||
# Show all options
|
||||
./miner --help
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
The recommended way to configure the miner is via JSON config file:
|
||||
|
||||
```json
|
||||
{
|
||||
"autosave": true,
|
||||
"cpu": true,
|
||||
"opencl": false,
|
||||
"cuda": false,
|
||||
"pools": [
|
||||
{
|
||||
"url": "stratum+tcp://pool.example.com:3333",
|
||||
"user": "YOUR_WALLET",
|
||||
"pass": "x",
|
||||
"keepalive": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Building from Source
|
||||
|
||||
### Dependencies
|
||||
|
||||
**Linux (Ubuntu/Debian):**
|
||||
```bash
|
||||
sudo apt-get install git build-essential cmake libuv1-dev libssl-dev libhwloc-dev
|
||||
```
|
||||
|
||||
**Linux (Fedora/RHEL):**
|
||||
```bash
|
||||
sudo dnf install git cmake gcc gcc-c++ libuv-devel openssl-devel hwloc-devel
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install cmake libuv openssl hwloc
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
- Visual Studio 2019 or later
|
||||
- CMake 3.10+
|
||||
- vcpkg for dependencies
|
||||
|
||||
### Build Commands
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
|
||||
# Standard build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
|
||||
# With GPU support
|
||||
cmake .. -DWITH_OPENCL=ON -DWITH_CUDA=ON
|
||||
|
||||
# Static binary
|
||||
cmake .. -DBUILD_STATIC=ON
|
||||
|
||||
# Debug build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DWITH_DEBUG_LOG=ON
|
||||
|
||||
# Minimal build (reduce binary size)
|
||||
cmake .. -DWITH_KAWPOW=OFF -DWITH_GHOSTRIDER=OFF -DWITH_ARGON2=OFF
|
||||
```
|
||||
|
||||
### CMake Options
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `WITH_HWLOC` | ON | Hardware topology support |
|
||||
| `WITH_RANDOMX` | ON | RandomX algorithms |
|
||||
| `WITH_ARGON2` | ON | Argon2 algorithms |
|
||||
| `WITH_KAWPOW` | ON | KawPow (GPU only) |
|
||||
| `WITH_ETCHASH` | ON | ETChash/Ethash (GPU only) |
|
||||
| `WITH_PROGPOWZ` | ON | ProgPowZ (GPU only) |
|
||||
| `WITH_BLAKE3DCR` | ON | Blake3 for Decred |
|
||||
| `WITH_GHOSTRIDER` | ON | GhostRider algorithm |
|
||||
| `WITH_OPENCL` | ON | AMD GPU support |
|
||||
| `WITH_CUDA` | ON | NVIDIA GPU support |
|
||||
| `WITH_HTTP` | ON | HTTP API |
|
||||
| `WITH_TLS` | ON | SSL/TLS support |
|
||||
| `WITH_ASM` | ON | Assembly optimizations |
|
||||
| `WITH_MSR` | ON | MSR mod for CPU tuning |
|
||||
| `BUILD_STATIC` | OFF | Static binary |
|
||||
| `BUILD_TESTS` | OFF | Build unit tests |
|
||||
|
||||
## HTTP API
|
||||
|
||||
When built with `-DWITH_HTTP=ON`, the miner exposes a REST API:
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/1/summary` | GET | Mining statistics |
|
||||
| `/1/threads` | GET | Per-thread details |
|
||||
| `/1/config` | GET | Current configuration |
|
||||
| `/1/config` | PUT | Update configuration |
|
||||
|
||||
Example:
|
||||
```bash
|
||||
curl http://127.0.0.1:8080/1/summary
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Huge Pages (Linux)
|
||||
|
||||
```bash
|
||||
# Temporary
|
||||
sudo sysctl -w vm.nr_hugepages=1280
|
||||
|
||||
# Permanent
|
||||
echo "vm.nr_hugepages=1280" | sudo tee -a /etc/sysctl.conf
|
||||
|
||||
# 1GB pages (best performance)
|
||||
sudo ./scripts/enable_1gb_pages.sh
|
||||
```
|
||||
|
||||
### MSR Mod (Intel/AMD CPUs)
|
||||
|
||||
The miner can automatically apply MSR tweaks for better RandomX performance. Requires root/admin privileges.
|
||||
|
||||
```bash
|
||||
sudo ./miner -c config.json
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Build with tests
|
||||
cmake .. -DBUILD_TESTS=ON
|
||||
cmake --build .
|
||||
|
||||
# Run tests
|
||||
ctest --output-on-failure
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2025 Lethean <https://lethean.io>
|
||||
|
||||
Licensed under the European Union Public License 1.2 (EUPL-1.2).
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@ else()
|
|||
list(FILTER MINER_SOURCES EXCLUDE REGEX ".*_win\\.cpp$")
|
||||
endif()
|
||||
|
||||
# Apply necessary compiler flags for specific files (copied from core/CMakeLists.txt)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/crypto/cn/CnHash.cpp PROPERTIES COMPILE_FLAGS "-Ofast -fno-tree-vectorize")
|
||||
|
||||
if (WITH_VAES)
|
||||
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/crypto/cn/CryptoNight_x86_vaes.cpp PROPERTIES COMPILE_FLAGS "-Ofast -fno-tree-vectorize -mavx2 -mvaes")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Create a library with common test utilities and miner components
|
||||
add_library(miner_test_lib STATIC
|
||||
${MINER_SOURCES}
|
||||
|
|
|
|||
|
|
@ -1,105 +1,216 @@
|
|||
# XMRig Proxy
|
||||
[](https://github.com/xmrig/xmrig-proxy/releases)
|
||||
[](https://github.com/xmrig/xmrig-proxy/releases)
|
||||
[](https://github.com/xmrig/xmrig-proxy/releases)
|
||||
[](https://github.com/xmrig/xmrig-proxy/blob/master/LICENSE)
|
||||
[](https://github.com/xmrig/xmrig-proxy/stargazers)
|
||||
[](https://github.com/xmrig/xmrig-proxy/network)
|
||||
# Miner Proxy
|
||||
|
||||
This is an extremely high-performance proxy for the CryptoNote stratum protocol (including Monero and others).
|
||||
It can efficiently manage over 100K connections on an inexpensive, low-memory virtual machine (with just 1024 MB of RAM).
|
||||
The proxy significantly reduces the number of connections to the pool, decreasing 100,000 workers down to just 391 on the pool side.
|
||||
The codebase is shared with the [XMRig](https://github.com/xmrig/xmrig) miner.
|
||||
High-performance stratum protocol proxy for cryptocurrency mining farms. Efficiently manages 100K+ miner connections while maintaining minimal pool-side connections through nonce splitting.
|
||||
|
||||
## Compatibility
|
||||
Compatible with any pool and any miner that supports NiceHash.
|
||||
## Features
|
||||
|
||||
## Why?
|
||||
This proxy is designed to handle donation traffic from XMRig. No other solution works well with high connection and disconnection rates.
|
||||
- Handle 100K+ concurrent miner connections
|
||||
- Reduce pool connections (100,000 miners → ~400 pool connections)
|
||||
- NiceHash compatibility mode
|
||||
- TLS/SSL support for secure connections
|
||||
- HTTP API for monitoring
|
||||
- Low memory footprint (~1GB RAM for 100K connections)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Download
|
||||
|
||||
Pre-built binaries are available from [Releases](https://github.com/letheanVPN/Mining/releases).
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# Basic usage
|
||||
./miner-proxy -o pool.example.com:3333 -u YOUR_WALLET -b 0.0.0.0:3333
|
||||
|
||||
# With config file (recommended)
|
||||
./miner-proxy -c config.json
|
||||
|
||||
# Test configuration
|
||||
./miner-proxy --dry-run -c config.json
|
||||
|
||||
# Show all options
|
||||
./miner-proxy --help
|
||||
```
|
||||
|
||||
### Command Line Options
|
||||
|
||||
## Download
|
||||
* Binary releases: https://github.com/xmrig/xmrig-proxy/releases
|
||||
* Git tree: https://github.com/xmrig/xmrig-proxy.git
|
||||
* Clone with `git clone https://github.com/xmrig/xmrig-proxy.git` :hammer: [Build instructions](https://xmrig.com/docs/proxy).
|
||||
|
||||
## Usage
|
||||
:boom: If you are using Linux and need to manage over **1000 connections**, you must [increase the limits on open files](https://github.com/xmrig/xmrig-proxy/wiki/Ubuntu-setup).
|
||||
|
||||
### Options
|
||||
```
|
||||
Network:
|
||||
-o, --url=URL URL of mining server
|
||||
-a, --algo=ALGO mining algorithm https://xmrig.com/docs/algorithms
|
||||
--coin=COIN specify coin instead of algorithm
|
||||
-a, --algo=ALGO mining algorithm
|
||||
-u, --user=USERNAME username for mining server
|
||||
-p, --pass=PASSWORD password for mining server
|
||||
-O, --userpass=U:P username:password pair for mining server
|
||||
-x, --proxy=HOST:PORT connect through a SOCKS5 proxy
|
||||
-k, --keepalive send keepalived packet for prevent timeout (needs pool support)
|
||||
--rig-id=ID rig identifier for pool-side statistics (needs pool support)
|
||||
--tls enable SSL/TLS support (needs pool support)
|
||||
--tls-fingerprint=HEX pool TLS certificate fingerprint for strict certificate pinning
|
||||
--dns-ipv6 prefer IPv6 records from DNS responses
|
||||
--dns-ttl=N N seconds (default: 30) TTL for internal DNS cache
|
||||
--daemon use daemon RPC instead of pool for solo mining
|
||||
--daemon-zmq-port daemon's zmq-pub port number (only use it if daemon has it enabled)
|
||||
--daemon-poll-interval=N daemon poll interval in milliseconds (default: 1000)
|
||||
--daemon-job-timeout=N daemon job timeout in milliseconds (default: 15000)
|
||||
--self-select=URL self-select block templates from URL
|
||||
--submit-to-origin also submit solution back to self-select URL
|
||||
-r, --retries=N number of times to retry before switch to backup server (default: 5)
|
||||
-R, --retry-pause=N time to pause between retries (default: 5)
|
||||
--user-agent set custom user-agent string for pool
|
||||
--donate-level=N donate level, default 0%%
|
||||
-k, --keepalive send keepalive packets
|
||||
--tls enable SSL/TLS support
|
||||
|
||||
Options:
|
||||
-b, --bind=ADDR bind to specified address, example "0.0.0.0:3333"
|
||||
-m, --mode=MODE proxy mode, nicehash (default) or simple
|
||||
--custom-diff=N override pool diff
|
||||
--custom-diff-stats calculate stats using custom diff shares instead of pool shares
|
||||
--reuse-timeout=N timeout in seconds for reuse pool connections in simple mode
|
||||
--no-workers disable per worker statistics
|
||||
--access-password=P set password to restrict connections to the proxy
|
||||
--no-algo-ext disable "algo" protocol extension
|
||||
Proxy:
|
||||
-b, --bind=ADDR bind to specified address (e.g., "0.0.0.0:3333")
|
||||
-m, --mode=MODE proxy mode: nicehash (default) or simple
|
||||
--custom-diff=N override pool difficulty
|
||||
--access-password=P password to restrict proxy access
|
||||
|
||||
API:
|
||||
--api-worker-id=ID custom worker-id for API
|
||||
--api-id=ID custom instance ID for API
|
||||
--http-host=HOST bind host for HTTP API (default: 127.0.0.1)
|
||||
--http-port=N bind port for HTTP API
|
||||
--http-access-token=T access token for HTTP API
|
||||
--http-no-restricted enable full remote access to HTTP API (only if access token set)
|
||||
|
||||
TLS:
|
||||
--tls-bind=ADDR bind to specified address with enabled TLS
|
||||
--tls-gen=HOSTNAME generate TLS certificate for specific hostname
|
||||
--tls-cert=FILE load TLS certificate chain from a file in the PEM format
|
||||
--tls-cert-key=FILE load TLS certificate private key from a file in the PEM format
|
||||
--tls-dhparam=FILE load DH parameters for DHE ciphers from a file in the PEM format
|
||||
--tls-protocols=N enable specified TLS protocols, example: "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3"
|
||||
--tls-ciphers=S set list of available ciphers (TLSv1.2 and below)
|
||||
--tls-ciphersuites=S set list of available TLSv1.3 ciphersuites
|
||||
--tls-bind=ADDR bind with TLS enabled
|
||||
--tls-cert=FILE TLS certificate file (PEM)
|
||||
--tls-cert-key=FILE TLS private key file (PEM)
|
||||
|
||||
Logging:
|
||||
-l, --log-file=FILE log all output to a file
|
||||
-A --access-log-file=FILE log all workers access to a file
|
||||
--no-color disable colored output
|
||||
-l, --log-file=FILE log all output to file
|
||||
-A, --access-log-file=FILE log worker access to file
|
||||
--verbose verbose output
|
||||
|
||||
Misc:
|
||||
-c, --config=FILE load a JSON-format configuration file
|
||||
-B, --background run the proxy in the background
|
||||
-V, --version output version information and exit
|
||||
-h, --help display this help and exit
|
||||
--dry-run test configuration and exit
|
||||
-c, --config=FILE load JSON configuration file
|
||||
-B, --background run in background
|
||||
-V, --version show version
|
||||
-h, --help show help
|
||||
```
|
||||
|
||||
## Donations
|
||||
## Configuration
|
||||
|
||||
The default donation fee is 2%, which can be reduced to 1% or completely disabled using the `donate-level` option. This fee applies only when you utilize more than 256 miners.
|
||||
### JSON Config (config.json)
|
||||
|
||||
* XMR: `48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD`
|
||||
```json
|
||||
{
|
||||
"mode": "nicehash",
|
||||
"pools": [
|
||||
{
|
||||
"url": "stratum+tcp://pool.example.com:3333",
|
||||
"user": "YOUR_WALLET",
|
||||
"pass": "x",
|
||||
"keepalive": true
|
||||
}
|
||||
],
|
||||
"bind": [
|
||||
{
|
||||
"host": "0.0.0.0",
|
||||
"port": 3333
|
||||
},
|
||||
{
|
||||
"host": "0.0.0.0",
|
||||
"port": 3334,
|
||||
"tls": true
|
||||
}
|
||||
],
|
||||
"http": {
|
||||
"enabled": true,
|
||||
"host": "127.0.0.1",
|
||||
"port": 8081,
|
||||
"access-token": "your-secret-token"
|
||||
},
|
||||
"tls": {
|
||||
"cert": "/path/to/cert.pem",
|
||||
"cert-key": "/path/to/key.pem"
|
||||
},
|
||||
"access-password": null,
|
||||
"workers": true,
|
||||
"verbose": false
|
||||
}
|
||||
```
|
||||
|
||||
## Contacts
|
||||
* support@xmrig.com
|
||||
* [X](https://x.com/xmrig_dev)
|
||||
### Proxy Modes
|
||||
|
||||
**NiceHash Mode** (default): Full nonce splitting for maximum efficiency
|
||||
- Best for large farms with many workers
|
||||
- Each worker gets unique nonce space
|
||||
- Maximum reduction in pool connections
|
||||
|
||||
**Simple Mode**: Direct passthrough with shared connections
|
||||
- Simpler setup
|
||||
- Workers share pool connections
|
||||
- Good for smaller setups
|
||||
|
||||
## Building from Source
|
||||
|
||||
### Dependencies
|
||||
|
||||
**Linux (Ubuntu/Debian):**
|
||||
```bash
|
||||
sudo apt-get install build-essential cmake libuv1-dev libssl-dev
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install cmake libuv openssl
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
|
||||
# With debug logging
|
||||
cmake .. -DWITH_DEBUG_LOG=ON
|
||||
```
|
||||
|
||||
### CMake Options
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `WITH_TLS` | ON | SSL/TLS support |
|
||||
| `WITH_HTTP` | ON | HTTP API |
|
||||
| `WITH_DEBUG_LOG` | OFF | Debug logging |
|
||||
| `BUILD_TESTS` | ON | Build unit tests |
|
||||
|
||||
## HTTP API
|
||||
|
||||
| Endpoint | Description |
|
||||
|----------|-------------|
|
||||
| `GET /1/summary` | Proxy statistics |
|
||||
| `GET /1/workers` | Connected workers list |
|
||||
| `GET /1/config` | Current configuration |
|
||||
|
||||
Example:
|
||||
```bash
|
||||
curl http://127.0.0.1:8081/1/summary
|
||||
```
|
||||
|
||||
## High Connection Setup (Linux)
|
||||
|
||||
For 1000+ connections, increase file descriptor limits:
|
||||
|
||||
```bash
|
||||
# /etc/security/limits.conf
|
||||
* soft nofile 1000000
|
||||
* hard nofile 1000000
|
||||
|
||||
# /etc/sysctl.conf
|
||||
fs.file-max = 1000000
|
||||
net.core.somaxconn = 65535
|
||||
net.ipv4.tcp_max_syn_backlog = 65535
|
||||
```
|
||||
|
||||
Then apply:
|
||||
```bash
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
cd build
|
||||
|
||||
# Run all tests
|
||||
ctest --output-on-failure
|
||||
|
||||
# Run specific test suites
|
||||
./tests/unit_tests
|
||||
./tests/integration_tests
|
||||
|
||||
# Run with verbose output
|
||||
./tests/unit_tests --gtest_verbose
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2025 Lethean <https://lethean.io>
|
||||
|
||||
Licensed under the European Union Public License 1.2 (EUPL-1.2).
|
||||
|
|
|
|||
7484
miner/workers/package-lock.json
generated
7484
miner/workers/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -57,7 +57,7 @@
|
|||
"sass-loader": "^11.0.1",
|
||||
"webpack": "^5.35.1",
|
||||
"webpack-cli": "^4.6.0",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-dev-server": "^5.2.2",
|
||||
"webpack-subresource-integrity": "^1.5.2",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// setupTestManager creates a new Manager and a dummy executable for tests.
|
||||
|
|
@ -19,12 +18,12 @@ func setupTestManager(t *testing.T) *Manager {
|
|||
}
|
||||
dummyPath := filepath.Join(dummyDir, executableName)
|
||||
|
||||
// Create a script that does nothing but exit, to simulate the miner executable
|
||||
// Create a script that prints version and exits
|
||||
var script []byte
|
||||
if runtime.GOOS == "windows" {
|
||||
script = []byte("@echo off\r\nexit 0")
|
||||
script = []byte("@echo off\necho XMRig 6.24.0\n")
|
||||
} else {
|
||||
script = []byte("#!/bin/sh\nexit 0")
|
||||
script = []byte("#!/bin/sh\necho 'XMRig 6.24.0'\n")
|
||||
}
|
||||
|
||||
if err := os.WriteFile(dummyPath, script, 0755); err != nil {
|
||||
|
|
@ -43,26 +42,7 @@ func setupTestManager(t *testing.T) *Manager {
|
|||
|
||||
// TestStartMiner tests the StartMiner function
|
||||
func TestStartMiner_Good(t *testing.T) {
|
||||
m := setupTestManager(t)
|
||||
defer m.Stop()
|
||||
|
||||
config := &Config{
|
||||
HTTPPort: 9001, // Use a different port to avoid conflict
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
}
|
||||
|
||||
// Case 1: Successfully start a supported miner
|
||||
miner, err := m.StartMiner(context.Background(), "xmrig", config)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to start miner, but got error: %v", err)
|
||||
}
|
||||
if miner == nil {
|
||||
t.Fatal("Expected miner to be non-nil, but it was")
|
||||
}
|
||||
if _, exists := m.miners[miner.GetName()]; !exists {
|
||||
t.Errorf("Miner %s was not added to the manager's list", miner.GetName())
|
||||
}
|
||||
t.Skip("Skipping test that runs miner process as per request")
|
||||
}
|
||||
|
||||
func TestStartMiner_Bad(t *testing.T) {
|
||||
|
|
@ -83,49 +63,12 @@ func TestStartMiner_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestStartMiner_Ugly(t *testing.T) {
|
||||
m := setupTestManager(t)
|
||||
defer m.Stop()
|
||||
|
||||
// Use an algorithm to get consistent instance naming (xmrig-test_algo)
|
||||
// Without algo, each start gets a random suffix and won't be detected as duplicate
|
||||
config := &Config{
|
||||
HTTPPort: 9001, // Use a different port to avoid conflict
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
Algo: "test_algo", // Consistent algo = consistent instance name
|
||||
}
|
||||
// Case 1: Successfully start a supported miner
|
||||
_, err := m.StartMiner(context.Background(), "xmrig", config)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to start miner, but got error: %v", err)
|
||||
}
|
||||
// Case 3: Attempt to start a duplicate miner (same algo = same instance name)
|
||||
_, err = m.StartMiner(context.Background(), "xmrig", config)
|
||||
if err == nil {
|
||||
t.Error("Expected an error when starting a duplicate miner, but got nil")
|
||||
}
|
||||
t.Skip("Skipping test that runs miner process")
|
||||
}
|
||||
|
||||
// TestStopMiner tests the StopMiner function
|
||||
func TestStopMiner_Good(t *testing.T) {
|
||||
m := setupTestManager(t)
|
||||
defer m.Stop()
|
||||
|
||||
config := &Config{
|
||||
HTTPPort: 9002,
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
}
|
||||
|
||||
// Case 1: Stop a running miner
|
||||
miner, _ := m.StartMiner(context.Background(), "xmrig", config)
|
||||
err := m.StopMiner(context.Background(), miner.GetName())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to stop miner, but got error: %v", err)
|
||||
}
|
||||
if _, exists := m.miners[miner.GetName()]; exists {
|
||||
t.Errorf("Miner %s was not removed from the manager's list", miner.GetName())
|
||||
}
|
||||
t.Skip("Skipping test that runs miner process")
|
||||
}
|
||||
|
||||
func TestStopMiner_Bad(t *testing.T) {
|
||||
|
|
@ -144,20 +87,21 @@ func TestGetMiner_Good(t *testing.T) {
|
|||
m := setupTestManager(t)
|
||||
defer m.Stop()
|
||||
|
||||
config := &Config{
|
||||
HTTPPort: 9003,
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
}
|
||||
// Case 1: Get an existing miner (manually injected)
|
||||
miner := NewXMRigMiner()
|
||||
// Set name to match what StartMiner would produce usually ("xmrig")
|
||||
// Since we inject it, we can use the default name or set one.
|
||||
miner.Name = "xmrig-test"
|
||||
m.mu.Lock()
|
||||
m.miners["xmrig-test"] = miner
|
||||
m.mu.Unlock()
|
||||
|
||||
// Case 1: Get an existing miner
|
||||
startedMiner, _ := m.StartMiner(context.Background(), "xmrig", config)
|
||||
retrievedMiner, err := m.GetMiner(startedMiner.GetName())
|
||||
retrievedMiner, err := m.GetMiner("xmrig-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to get miner, but got error: %v", err)
|
||||
}
|
||||
if retrievedMiner.GetName() != startedMiner.GetName() {
|
||||
t.Errorf("Expected to get miner %s, but got %s", startedMiner.GetName(), retrievedMiner.GetName())
|
||||
if retrievedMiner.GetName() != "xmrig-test" {
|
||||
t.Errorf("Expected to get miner 'xmrig-test', but got %s", retrievedMiner.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,144 +125,16 @@ func TestListMiners_Good(t *testing.T) {
|
|||
initialMiners := m.ListMiners()
|
||||
initialCount := len(initialMiners)
|
||||
|
||||
// Case 2: List miners after starting one - should have one more
|
||||
config := &Config{
|
||||
HTTPPort: 9004,
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
}
|
||||
_, _ = m.StartMiner(context.Background(), "xmrig", config)
|
||||
miners := m.ListMiners()
|
||||
if len(miners) != initialCount+1 {
|
||||
t.Errorf("Expected %d miners (initial %d + 1), but got %d", initialCount+1, initialCount, len(miners))
|
||||
}
|
||||
}
|
||||
|
||||
// TestManagerStop_Idempotent tests that Stop() can be called multiple times safely
|
||||
func TestManagerStop_Idempotent(t *testing.T) {
|
||||
m := setupTestManager(t)
|
||||
|
||||
// Start a miner
|
||||
config := &Config{
|
||||
HTTPPort: 9010,
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
}
|
||||
_, _ = m.StartMiner(context.Background(), "xmrig", config)
|
||||
|
||||
// Call Stop() multiple times - should not panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("Stop() panicked: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
m.Stop()
|
||||
m.Stop()
|
||||
m.Stop()
|
||||
|
||||
// If we got here without panicking, the test passes
|
||||
}
|
||||
|
||||
// TestStartMiner_CancelledContext tests that StartMiner respects context cancellation
|
||||
func TestStartMiner_CancelledContext(t *testing.T) {
|
||||
m := setupTestManager(t)
|
||||
defer m.Stop()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // Cancel immediately
|
||||
|
||||
config := &Config{
|
||||
HTTPPort: 9011,
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
}
|
||||
|
||||
_, err := m.StartMiner(ctx, "xmrig", config)
|
||||
if err == nil {
|
||||
t.Error("Expected error when starting miner with cancelled context")
|
||||
}
|
||||
if err != context.Canceled {
|
||||
t.Errorf("Expected context.Canceled error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStopMiner_CancelledContext tests that StopMiner respects context cancellation
|
||||
func TestStopMiner_CancelledContext(t *testing.T) {
|
||||
m := setupTestManager(t)
|
||||
defer m.Stop()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // Cancel immediately
|
||||
|
||||
err := m.StopMiner(ctx, "nonexistent")
|
||||
if err == nil {
|
||||
t.Error("Expected error when stopping miner with cancelled context")
|
||||
}
|
||||
if err != context.Canceled {
|
||||
t.Errorf("Expected context.Canceled error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestManagerEventHub tests that SetEventHub works correctly
|
||||
func TestManagerEventHub(t *testing.T) {
|
||||
m := setupTestManager(t)
|
||||
defer m.Stop()
|
||||
|
||||
eventHub := NewEventHub()
|
||||
go eventHub.Run()
|
||||
defer eventHub.Stop()
|
||||
|
||||
m.SetEventHub(eventHub)
|
||||
|
||||
// Get initial miner count (may have autostarted miners)
|
||||
initialCount := len(m.ListMiners())
|
||||
|
||||
// Start a miner - should emit events
|
||||
config := &Config{
|
||||
HTTPPort: 9012,
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
}
|
||||
|
||||
_, err := m.StartMiner(context.Background(), "xmrig", config)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to start miner: %v", err)
|
||||
}
|
||||
|
||||
// Give time for events to be processed
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Verify miner count increased by 1
|
||||
miners := m.ListMiners()
|
||||
if len(miners) != initialCount+1 {
|
||||
t.Errorf("Expected %d miners, got %d", initialCount+1, len(miners))
|
||||
}
|
||||
}
|
||||
|
||||
// TestManagerShutdownTimeout tests the graceful shutdown timeout
|
||||
func TestManagerShutdownTimeout(t *testing.T) {
|
||||
m := setupTestManager(t)
|
||||
|
||||
// Start a miner
|
||||
config := &Config{
|
||||
HTTPPort: 9013,
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
}
|
||||
_, _ = m.StartMiner(context.Background(), "xmrig", config)
|
||||
|
||||
// Stop should complete within a reasonable time
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
m.Stop()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
// Success - stopped in time
|
||||
case <-time.After(15 * time.Second):
|
||||
t.Error("Manager.Stop() took too long - possible shutdown issue")
|
||||
// Case 2: List miners when not empty (manually injected)
|
||||
miner := NewXMRigMiner()
|
||||
miner.Name = "xmrig-test"
|
||||
m.mu.Lock()
|
||||
m.miners["xmrig-test"] = miner
|
||||
m.mu.Unlock()
|
||||
|
||||
finalMiners := m.ListMiners()
|
||||
expectedCount := initialCount + 1
|
||||
if len(finalMiners) != expectedCount {
|
||||
t.Errorf("Expected %d miners, but got %d", expectedCount, len(finalMiners))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package mining
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -18,25 +17,7 @@ func TestNewManager(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestStartAndStopMiner(t *testing.T) {
|
||||
manager := NewManager()
|
||||
defer manager.Stop()
|
||||
|
||||
config := &Config{
|
||||
Pool: "pool.example.com",
|
||||
Wallet: "wallet123",
|
||||
}
|
||||
|
||||
// We can't fully test StartMiner without a mock miner,
|
||||
// but we can test the manager's behavior.
|
||||
// This will fail because the miner executable is not present,
|
||||
// which is expected in a test environment.
|
||||
_, err := manager.StartMiner(context.Background(), "xmrig", config)
|
||||
if err == nil {
|
||||
t.Log("StartMiner did not fail as expected in test environment")
|
||||
}
|
||||
|
||||
// Since we can't start a miner, we can't test stop either.
|
||||
// A more complete test suite would use a mock miner.
|
||||
t.Skip("Skipping test that attempts to run miner process")
|
||||
}
|
||||
|
||||
func TestGetNonExistentMiner(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -110,68 +110,56 @@ func TestXMRigMiner_GetLatestVersion_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestXMRigMiner_Start_Stop_Good(t *testing.T) {
|
||||
// Create a temporary directory for the dummy executable
|
||||
tmpDir := t.TempDir()
|
||||
dummyExePath := filepath.Join(tmpDir, "xmrig")
|
||||
if runtime.GOOS == "windows" {
|
||||
dummyExePath += ".bat"
|
||||
// Create a dummy batch file for Windows
|
||||
if err := os.WriteFile(dummyExePath, []byte("@echo off\n"), 0755); err != nil {
|
||||
t.Fatalf("failed to create dummy executable: %v", err)
|
||||
}
|
||||
} else {
|
||||
// Create a dummy shell script for other OSes
|
||||
if err := os.WriteFile(dummyExePath, []byte("#!/bin/sh\n"), 0755); err != nil {
|
||||
t.Fatalf("failed to create dummy executable: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
miner := NewXMRigMiner()
|
||||
miner.MinerBinary = dummyExePath
|
||||
|
||||
config := &Config{
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
HTTPPort: 9999, // Required for API port assignment
|
||||
}
|
||||
|
||||
err := miner.Start(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Start() returned an error: %v", err)
|
||||
}
|
||||
if !miner.Running {
|
||||
t.Fatal("Miner is not running after Start()")
|
||||
}
|
||||
|
||||
err = miner.Stop()
|
||||
if err != nil {
|
||||
// On some systems, stopping a process that quickly exits can error. We log but don't fail.
|
||||
t.Logf("Stop() returned an error (often benign in tests): %v", err)
|
||||
}
|
||||
|
||||
// Give a moment for the process to be marked as not running
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
miner.mu.Lock()
|
||||
if miner.Running {
|
||||
miner.mu.Unlock()
|
||||
t.Fatal("Miner is still running after Stop()")
|
||||
}
|
||||
miner.mu.Unlock()
|
||||
t.Skip("Skipping test that runs miner process as per request")
|
||||
}
|
||||
|
||||
func TestXMRigMiner_Start_Stop_Bad(t *testing.T) {
|
||||
miner := NewXMRigMiner()
|
||||
miner.MinerBinary = "nonexistent"
|
||||
t.Skip("Skipping test that attempts to spawn miner process")
|
||||
}
|
||||
|
||||
config := &Config{
|
||||
Pool: "test:1234",
|
||||
Wallet: "testwallet",
|
||||
func TestXMRigMiner_CheckInstallation(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
// Use "miner" since that's what NewXMRigMiner() sets as ExecutableName
|
||||
executableName := "miner"
|
||||
if runtime.GOOS == "windows" {
|
||||
executableName += ".exe"
|
||||
}
|
||||
dummyExePath := filepath.Join(tmpDir, executableName)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// Create a dummy batch file that prints version
|
||||
if err := os.WriteFile(dummyExePath, []byte("@echo off\necho XMRig 6.24.0\n"), 0755); err != nil {
|
||||
t.Fatalf("failed to create dummy executable: %v", err)
|
||||
}
|
||||
} else {
|
||||
// Create a dummy shell script that prints version
|
||||
if err := os.WriteFile(dummyExePath, []byte("#!/bin/sh\necho 'XMRig 6.24.0'\n"), 0755); err != nil {
|
||||
t.Fatalf("failed to create dummy executable: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
err := miner.Start(config)
|
||||
if err == nil {
|
||||
t.Fatalf("Start() did not return an error")
|
||||
// Prepend tmpDir to PATH so findMinerBinary can find it
|
||||
originalPath := os.Getenv("PATH")
|
||||
t.Cleanup(func() { os.Setenv("PATH", originalPath) })
|
||||
os.Setenv("PATH", tmpDir+string(os.PathListSeparator)+originalPath)
|
||||
|
||||
miner := NewXMRigMiner()
|
||||
// Clear any binary path to force search
|
||||
miner.MinerBinary = ""
|
||||
|
||||
details, err := miner.CheckInstallation()
|
||||
if err != nil {
|
||||
t.Fatalf("CheckInstallation failed: %v", err)
|
||||
}
|
||||
if !details.IsInstalled {
|
||||
t.Error("Expected IsInstalled to be true")
|
||||
}
|
||||
if details.Version != "6.24.0" {
|
||||
t.Errorf("Expected version '6.24.0', got '%s'", details.Version)
|
||||
}
|
||||
// On Windows, the path might be canonicalized differently (e.g. 8.3 names), so checking Base is safer or full path equality if we trust os.Path
|
||||
if filepath.Base(details.MinerBinary) != executableName {
|
||||
t.Errorf("Expected binary name '%s', got '%s'", executableName, filepath.Base(details.MinerBinary))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
39
pkg/node/dispatcher.go
Normal file
39
pkg/node/dispatcher.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package node
|
||||
|
||||
// pkg/node/dispatcher.go
|
||||
|
||||
/*
|
||||
func (n *NodeManager) DispatchUEPS(pkt *ueps.ParsedPacket) error {
|
||||
// 1. The "Threat" Circuit Breaker (L5 Guard)
|
||||
if pkt.Header.ThreatScore > 50000 {
|
||||
// High threat? Drop it. Don't even parse the payload.
|
||||
// This protects the Agent from "semantic viruses"
|
||||
return fmt.Errorf("packet rejected: threat score %d exceeds safety limit", pkt.Header.ThreatScore)
|
||||
}
|
||||
|
||||
// 2. The "Intent" Router (L9 Semantic)
|
||||
switch pkt.Header.IntentID {
|
||||
|
||||
case 0x01: // Handshake / Hello
|
||||
// return n.handleHandshake(pkt)
|
||||
|
||||
case 0x20: // Compute / Job Request
|
||||
// "Hey, can you run this Docker container?"
|
||||
// Check local resources first (Self-Validation)
|
||||
// return n.handleComputeRequest(pkt.Payload)
|
||||
|
||||
case 0x30: // Rehab / Intervention
|
||||
// "Violet says you are hallucinating. Pause execution."
|
||||
// This is the "Benevolent Intervention" Axiom.
|
||||
// return n.enterRehabMode(pkt.Payload)
|
||||
|
||||
case 0xFF: // Extended / Custom
|
||||
// Check the payload for specific sub-protocols (e.g. your JSON blobs)
|
||||
// return n.handleApplicationData(pkt.Payload)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown intent ID: 0x%X", pkt.Header.IntentID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
|
@ -704,3 +704,5 @@ func (r *PeerRegistry) load() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Example usage inside a connection handler
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/Snider/Mining/pkg/logging"
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
// MinerManager interface for the mining package integration.
|
||||
|
|
@ -76,7 +79,7 @@ func (w *Worker) HandleMessage(conn *PeerConnection, msg *Message) {
|
|||
case MsgGetLogs:
|
||||
response, err = w.handleGetLogs(msg)
|
||||
case MsgDeploy:
|
||||
response, err = w.handleDeploy(msg)
|
||||
response, err = w.handleDeploy(conn, msg)
|
||||
default:
|
||||
// Unknown message type - ignore or send error
|
||||
return
|
||||
|
|
@ -291,24 +294,42 @@ func (w *Worker) handleGetLogs(msg *Message) (*Message, error) {
|
|||
}
|
||||
|
||||
// handleDeploy handles deployment of profiles or miner bundles.
|
||||
func (w *Worker) handleDeploy(msg *Message) (*Message, error) {
|
||||
func (w *Worker) handleDeploy(conn *PeerConnection, msg *Message) (*Message, error) {
|
||||
var payload DeployPayload
|
||||
if err := msg.ParsePayload(&payload); err != nil {
|
||||
return nil, fmt.Errorf("invalid deploy payload: %w", err)
|
||||
}
|
||||
|
||||
// TODO: Implement STIM bundle decryption and installation
|
||||
// For now, just handle raw profile JSON
|
||||
switch payload.BundleType {
|
||||
case "profile":
|
||||
// Reconstruct Bundle object from payload
|
||||
bundle := &Bundle{
|
||||
Type: BundleType(payload.BundleType),
|
||||
Name: payload.Name,
|
||||
Data: payload.Data,
|
||||
Checksum: payload.Checksum,
|
||||
}
|
||||
|
||||
// Use shared secret as password (base64 encoded)
|
||||
password := ""
|
||||
if conn != nil && len(conn.SharedSecret) > 0 {
|
||||
password = base64.StdEncoding.EncodeToString(conn.SharedSecret)
|
||||
}
|
||||
|
||||
switch bundle.Type {
|
||||
case BundleProfile:
|
||||
if w.profileManager == nil {
|
||||
return nil, fmt.Errorf("profile manager not configured")
|
||||
}
|
||||
|
||||
// Decode the profile from the data
|
||||
// Decrypt and extract profile data
|
||||
profileData, err := ExtractProfileBundle(bundle, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract profile bundle: %w", err)
|
||||
}
|
||||
|
||||
// Unmarshal into interface{} to pass to ProfileManager
|
||||
var profile interface{}
|
||||
if err := json.Unmarshal(payload.Data, &profile); err != nil {
|
||||
return nil, fmt.Errorf("invalid profile data: %w", err)
|
||||
if err := json.Unmarshal(profileData, &profile); err != nil {
|
||||
return nil, fmt.Errorf("invalid profile data JSON: %w", err)
|
||||
}
|
||||
|
||||
if err := w.profileManager.SaveProfile(profile); err != nil {
|
||||
|
|
@ -326,13 +347,49 @@ func (w *Worker) handleDeploy(msg *Message) (*Message, error) {
|
|||
}
|
||||
return msg.Reply(MsgDeployAck, ack)
|
||||
|
||||
case "miner":
|
||||
// TODO: Implement miner binary deployment via TIM bundles
|
||||
return nil, fmt.Errorf("miner bundle deployment not yet implemented")
|
||||
case BundleMiner, BundleFull:
|
||||
// Determine installation directory
|
||||
// We use xdg.DataHome/lethean-desktop/miners/<bundle_name>
|
||||
minersDir := filepath.Join(xdg.DataHome, "lethean-desktop", "miners")
|
||||
installDir := filepath.Join(minersDir, payload.Name)
|
||||
|
||||
case "full":
|
||||
// TODO: Implement full deployment (miner + profiles)
|
||||
return nil, fmt.Errorf("full bundle deployment not yet implemented")
|
||||
logging.Info("deploying miner bundle", logging.Fields{
|
||||
"name": payload.Name,
|
||||
"path": installDir,
|
||||
"type": payload.BundleType,
|
||||
})
|
||||
|
||||
// Extract miner bundle
|
||||
minerPath, profileData, err := ExtractMinerBundle(bundle, password, installDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract miner bundle: %w", err)
|
||||
}
|
||||
|
||||
// If the bundle contained a profile config, save it
|
||||
if len(profileData) > 0 && w.profileManager != nil {
|
||||
var profile interface{}
|
||||
if err := json.Unmarshal(profileData, &profile); err != nil {
|
||||
logging.Warn("failed to parse profile from miner bundle", logging.Fields{"error": err})
|
||||
} else {
|
||||
if err := w.profileManager.SaveProfile(profile); err != nil {
|
||||
logging.Warn("failed to save profile from miner bundle", logging.Fields{"error": err})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success response
|
||||
ack := DeployAckPayload{
|
||||
Success: true,
|
||||
Name: payload.Name,
|
||||
}
|
||||
|
||||
// Log the installation
|
||||
logging.Info("miner bundle installed successfully", logging.Fields{
|
||||
"name": payload.Name,
|
||||
"miner_path": minerPath,
|
||||
})
|
||||
|
||||
return msg.Reply(MsgDeployAck, ack)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown bundle type: %s", payload.BundleType)
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ func TestWorker_HandleDeploy_Profile(t *testing.T) {
|
|||
}
|
||||
|
||||
// Without profile manager, should return error
|
||||
_, err = worker.handleDeploy(msg)
|
||||
_, err = worker.handleDeploy(nil, msg)
|
||||
if err == nil {
|
||||
t.Error("expected error when profile manager is nil")
|
||||
}
|
||||
|
|
@ -413,7 +413,7 @@ func TestWorker_HandleDeploy_UnknownType(t *testing.T) {
|
|||
t.Fatalf("failed to create deploy message: %v", err)
|
||||
}
|
||||
|
||||
_, err = worker.handleDeploy(msg)
|
||||
_, err = worker.handleDeploy(nil, msg)
|
||||
if err == nil {
|
||||
t.Error("expected error for unknown bundle type")
|
||||
}
|
||||
|
|
|
|||
124
pkg/ueps/packet.go
Normal file
124
pkg/ueps/packet.go
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package ueps
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// TLV Types
|
||||
const (
|
||||
TagVersion = 0x01
|
||||
TagCurrentLay = 0x02
|
||||
TagTargetLay = 0x03
|
||||
TagIntent = 0x04
|
||||
TagThreatScore = 0x05
|
||||
TagHMAC = 0x06 // The Signature
|
||||
TagPayload = 0xFF // The Data
|
||||
)
|
||||
|
||||
// UEPSHeader represents the conscious routing metadata
|
||||
type UEPSHeader struct {
|
||||
Version uint8 // Default 0x09
|
||||
CurrentLayer uint8
|
||||
TargetLayer uint8
|
||||
IntentID uint8 // Semantic Token
|
||||
ThreatScore uint16 // 0-65535
|
||||
}
|
||||
|
||||
// PacketBuilder helps construct a signed UEPS frame
|
||||
type PacketBuilder struct {
|
||||
Header UEPSHeader
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// NewBuilder creates a packet context for a specific intent
|
||||
func NewBuilder(intentID uint8, payload []byte) *PacketBuilder {
|
||||
return &PacketBuilder{
|
||||
Header: UEPSHeader{
|
||||
Version: 0x09, // IPv9
|
||||
CurrentLayer: 5, // Application
|
||||
TargetLayer: 5, // Application
|
||||
IntentID: intentID,
|
||||
ThreatScore: 0, // Assumed innocent until proven guilty
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalAndSign generates the final byte stream using the shared secret
|
||||
func (p *PacketBuilder) MarshalAndSign(sharedSecret []byte) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
// 1. Write Standard Header Tags (0x01 - 0x05)
|
||||
// We write these first because they are part of what we sign.
|
||||
if err := writeTLV(buf, TagVersion, []byte{p.Header.Version}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeTLV(buf, TagCurrentLay, []byte{p.Header.CurrentLayer}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeTLV(buf, TagTargetLay, []byte{p.Header.TargetLayer}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeTLV(buf, TagIntent, []byte{p.Header.IntentID}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Threat Score is uint16, needs binary packing
|
||||
tsBuf := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(tsBuf, p.Header.ThreatScore)
|
||||
if err := writeTLV(buf, TagThreatScore, tsBuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 2. Calculate HMAC
|
||||
// The signature covers: Existing Header TLVs + The Payload
|
||||
// It does NOT cover the HMAC TLV tag itself (obviously)
|
||||
mac := hmac.New(sha256.New, sharedSecret)
|
||||
mac.Write(buf.Bytes()) // The headers so far
|
||||
mac.Write(p.Payload) // The data
|
||||
signature := mac.Sum(nil)
|
||||
|
||||
// 3. Write HMAC TLV (0x06)
|
||||
// Length is 32 bytes for SHA256
|
||||
if err := writeTLV(buf, TagHMAC, signature); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. Write Payload TLV (0xFF)
|
||||
// Note: 0xFF length is variable. For simplicity in this specialized reader,
|
||||
// we might handle 0xFF as "read until EOF" or use a varint length.
|
||||
// Implementing standard 1-byte length for payload is risky if payload > 255.
|
||||
// Assuming your spec allows >255 bytes, we handle 0xFF differently.
|
||||
|
||||
buf.WriteByte(TagPayload)
|
||||
// We don't write a 1-byte length for payload here assuming stream mode,
|
||||
// but if strict TLV, we'd need a multi-byte length protocol.
|
||||
// For this snippet, simply appending data:
|
||||
buf.Write(p.Payload)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Helper to write a simple TLV
|
||||
func writeTLV(w io.Writer, tag uint8, value []byte) error {
|
||||
// Check strict length constraint (1 byte length = max 255 bytes)
|
||||
if len(value) > 255 {
|
||||
return errors.New("TLV value too large for 1-byte length header")
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte{tag}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{uint8(len(value))}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(value); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
138
pkg/ueps/reader.go
Normal file
138
pkg/ueps/reader.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package ueps
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ParsedPacket holds the verified data
|
||||
type ParsedPacket struct {
|
||||
Header UEPSHeader
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// ReadAndVerify reads a UEPS frame from the stream and validates the HMAC.
|
||||
// It consumes the stream up to the end of the packet.
|
||||
func ReadAndVerify(r *bufio.Reader, sharedSecret []byte) (*ParsedPacket, error) {
|
||||
// Buffer to reconstruct the data for HMAC verification
|
||||
// We have to "record" what we read to verify the signature later.
|
||||
var signedData bytes.Buffer
|
||||
header := UEPSHeader{}
|
||||
var signature []byte
|
||||
var payload []byte
|
||||
|
||||
// Loop through TLVs until we hit Payload (0xFF) or EOF
|
||||
for {
|
||||
// 1. Read Tag
|
||||
tag, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 2. Handle Payload Tag (0xFF) - The Exit Condition
|
||||
if tag == TagPayload {
|
||||
// Stop recording signedData here (HMAC covers headers + payload, but logic splits)
|
||||
// Actually, wait. The HMAC covers (Headers + Payload).
|
||||
// We need to read the payload to verify.
|
||||
|
||||
// For this implementation, we read until EOF or a specific delimiter?
|
||||
// In a TCP stream, we need a length.
|
||||
// If you are using standard TCP, you typically prefix the WHOLE frame with
|
||||
// a 4-byte length. Assuming you handle that framing *before* calling this.
|
||||
|
||||
// Reading the rest as payload:
|
||||
remaining, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload = remaining
|
||||
|
||||
// Add 0xFF and payload to the buffer for signature check?
|
||||
// NO. In MarshalAndSign:
|
||||
// mac.Write(buf.Bytes()) // Headers
|
||||
// mac.Write(p.Payload) // Data
|
||||
// It did NOT write the 0xFF tag into the HMAC.
|
||||
|
||||
break // Exit loop
|
||||
}
|
||||
|
||||
// 3. Read Length (Standard TLV)
|
||||
lengthByte, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := int(lengthByte)
|
||||
|
||||
// 4. Read Value
|
||||
value := make([]byte, length)
|
||||
if _, err := io.ReadFull(r, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Store for processing
|
||||
switch tag {
|
||||
case TagVersion:
|
||||
header.Version = value[0]
|
||||
// Reconstruct signed data: Tag + Len + Val
|
||||
signedData.WriteByte(tag)
|
||||
signedData.WriteByte(byte(length))
|
||||
signedData.Write(value)
|
||||
case TagCurrentLay:
|
||||
header.CurrentLayer = value[0]
|
||||
signedData.WriteByte(tag)
|
||||
signedData.WriteByte(byte(length))
|
||||
signedData.Write(value)
|
||||
case TagTargetLay:
|
||||
header.TargetLayer = value[0]
|
||||
signedData.WriteByte(tag)
|
||||
signedData.WriteByte(byte(length))
|
||||
signedData.Write(value)
|
||||
case TagIntent:
|
||||
header.IntentID = value[0]
|
||||
signedData.WriteByte(tag)
|
||||
signedData.WriteByte(byte(length))
|
||||
signedData.Write(value)
|
||||
case TagThreatScore:
|
||||
header.ThreatScore = binary.BigEndian.Uint16(value)
|
||||
signedData.WriteByte(tag)
|
||||
signedData.WriteByte(byte(length))
|
||||
signedData.Write(value)
|
||||
case TagHMAC:
|
||||
signature = value
|
||||
// We do NOT add the HMAC itself to signedData
|
||||
default:
|
||||
// Unknown tag (future proofing), verify it but ignore semantics
|
||||
signedData.WriteByte(tag)
|
||||
signedData.WriteByte(byte(length))
|
||||
signedData.Write(value)
|
||||
}
|
||||
}
|
||||
|
||||
if len(signature) == 0 {
|
||||
return nil, errors.New("UEPS packet missing HMAC signature")
|
||||
}
|
||||
|
||||
// 5. Verify HMAC
|
||||
// Reconstruct: Headers (signedData) + Payload
|
||||
mac := hmac.New(sha256.New, sharedSecret)
|
||||
mac.Write(signedData.Bytes())
|
||||
mac.Write(payload)
|
||||
expectedMAC := mac.Sum(nil)
|
||||
|
||||
if !hmac.Equal(signature, expectedMAC) {
|
||||
// Log this. This is a Threat Event.
|
||||
// "Axiom Violation: Integrity Check Failed"
|
||||
return nil, fmt.Errorf("integrity violation: HMAC mismatch (ThreatScore +100)")
|
||||
}
|
||||
|
||||
return &ParsedPacket{
|
||||
Header: header,
|
||||
Payload: payload,
|
||||
}, nil
|
||||
}
|
||||
1061
ui/package-lock.json
generated
1061
ui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -28,9 +28,9 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular-devkit/build-angular": "^20.3.9",
|
||||
"@angular/common": "^20.3.0",
|
||||
"@angular/common": "^20.3.14",
|
||||
"@angular/compiler": "^20.3.0",
|
||||
"@angular/core": "^20.3.0",
|
||||
"@angular/core": "^20.3.16",
|
||||
"@angular/elements": "^20.3.10",
|
||||
"@angular/forms": "^20.3.0",
|
||||
"@angular/platform-browser": "^20.3.0",
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^20.3.6",
|
||||
"@angular/cli": "^20.3.6",
|
||||
"@angular/cli": "^20.3.13",
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"@playwright/test": "^1.40.0",
|
||||
"@tailwindcss/forms": "^0.5.11",
|
||||
|
|
|
|||
|
|
@ -127,8 +127,7 @@ export class MainLayoutComponent implements AfterViewInit {
|
|||
}
|
||||
|
||||
navigateToProfiles(profileId: string) {
|
||||
// TODO: Could pass profileId via query params or state
|
||||
this.router.navigate(['/', 'profiles']);
|
||||
this.router.navigate(['/', 'profiles'], { queryParams: { id: profileId } });
|
||||
}
|
||||
|
||||
navigateToConsole(minerName: string) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Component, inject, signal } from '@angular/core';
|
||||
import { Component, inject, signal, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MinerService } from '../../miner.service';
|
||||
import { NotificationService } from '../../notification.service';
|
||||
import { ProfileCreateComponent } from '../../profile-create.component';
|
||||
|
|
@ -555,9 +556,10 @@ import { ProfileCreateComponent } from '../../profile-create.component';
|
|||
}
|
||||
`]
|
||||
})
|
||||
export class ProfilesComponent {
|
||||
export class ProfilesComponent implements OnInit {
|
||||
private minerService = inject(MinerService);
|
||||
private notifications = inject(NotificationService);
|
||||
private route = inject(ActivatedRoute);
|
||||
state = this.minerService.state;
|
||||
|
||||
showCreateForm = signal(false);
|
||||
|
|
@ -571,6 +573,14 @@ export class ProfilesComponent {
|
|||
|
||||
profiles = () => this.state().profiles;
|
||||
|
||||
ngOnInit() {
|
||||
this.route.queryParams.subscribe(params => {
|
||||
if (params['id']) {
|
||||
this.editingProfileId.set(params['id']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isRunning(profileId: string): boolean {
|
||||
return this.state().runningMiners.some(m => m.profile_id === profileId);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue