diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index e60c80f42d..0000000000
--- a/.gitattributes
+++ /dev/null
@@ -1,192 +0,0 @@
-* text=auto
-
-###### Git
-.gitattributes text
-.gitignore text
-.gitconfig text
-.gitmodules text
-
-##### Windows
-*.bat text eol=crlf
-*.exe binary
-*.dll binary
-
-##### Linux
-*.sh text eol=lf
-*.so binary
-
-##### Global
-# Documents
-*.sql text
-*.md text
-*.adoc text
-*.textile text
-*.mustache text
-*.csv text
-*.tab text
-*.tsv text
-*.coffee text
-*.css text
-*.htm text
-*.html text
-*.xhtml text
-*.inc text
-*.js text
-*.jsx text
-*.less text
-*.od text
-*.onlydata text
-*.sass text
-*.scm text
-*.log text
-*.properties text
-*.scss text
-*.styl text
-*.tag text
-*.ts text
-*.tsx text
-*.dockerignore text
-Dockerfile text
-*.markdown text
-*.mdwn text
-*.mdown text
-*.mkd text
-*.mkdn text
-*.mdtxt text
-*.mdtext text
-*.txt text
-AUTHORS text
-CHANGELOG text
-CHANGES text
-CONTRIBUTING text
-COPYING text
-copyright text
-*COPYRIGHT* text
-INSTALL text
-license text
-LICENSE text
-NEWS text
-readme text
-*README* text
-TODO text
-# Configuration
-*.cnf text
-*.cfg text
-*.conf text
-*.config text
-*.ini text
-*.json text
-*.xml text
-*.bowerrc text
-.browserslistrc text
-.editorconfig text
-*.npmignore text
-*.yaml text
-*.yml text
-browserslist text
-Makefile text
-makefile text
-Procfile text
-.slugignore text
-# Linters
-.csslintrc text
-.eslintrc text
-.htmlhintrc text
-.jscsrc text
-.jshintrc text
-.jshintignore text
-.stylelintrc text
-# Video
-*.3gpp binary
-*.3gp binary
-*.as binary
-*.asf binary
-*.asx binary
-*.fla binary
-*.flv binary
-*.m4v binary
-*.mng binary
-*.mov binary
-*.mp4 binary
-*.mpeg binary
-*.mpg binary
-*.ogv binary
-*.swc binary
-*.swf binary
-*.webm binary
-# Audio
-*.kar binary
-*.m4a binary
-*.mid binary
-*.midi binary
-*.mp3 binary
-*.ogg binary
-*.ra binary
-# Graphics
-*.png binary
-*.jpg binary
-*.jpeg binary
-*.gif binary
-*.tif binary
-*.tiff binary
-*.ico binary
-*.eps binary
-*.ai binary
-*.bmp binary
-*.jng binary
-*.jp2 binary
-*.jpx binary
-*.jxr binary
-*.pdf binary
-*.psb binary
-*.psd binary
-*.svg text
-*.svgz binary
-*.wbmp binary
-*.webp binary
-# Archives
-*.7z binary
-*.gz binary
-*.jar binary
-*.rar binary
-*.tar binary
-*.zip binary
-# Fonts
-*.ttf binary
-*.eot binary
-*.otf binary
-*.woff binary
-*.woff2 binary
-# Executables
-*.pyc binary
-# Objects
-*.o binary
-
-##### IDE/Editor
-# Visual Studio
-*.sln text eol=crlf
-*.csproj text eol=crlf
-*.vbproj text eol=crlf
-*.vcxproj text eol=crlf
-*.vcproj text eol=crlf
-*.dbproj text eol=crlf
-*.fsproj text eol=crlf
-*.lsproj text eol=crlf
-*.wixproj text eol=crlf
-*.modelproj text eol=crlf
-*.sqlproj text eol=crlf
-*.wmaproj text eol=crlf
-*.xproj text eol=crlf
-*.props text eol=crlf
-*.filters text eol=crlf
-*.vcxitems text eol=crlf
-
-##### Language
-# .Net
-*.resx text
-*.settings text
-*.cs text
-*.vb text
-*.cpp text
-*.h text
-*.fs text
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 0bf0b0de53..0000000000
--- a/.gitignore
+++ /dev/null
@@ -1,214 +0,0 @@
-*.suo
-*.user
-*.sln.docstates
-.vs/
-.vscode/
-
-[Dd]ebug/
-[Rr]elease/
-x64/
-build/
-[Bb]in/
-[Oo]bj/
-*.VC.db
-*.VC.opendb
-
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-*_i.c
-*_p.c
-*.ilk
-*.meta
-*.obj
-*.o
-*.so
-*.pch
-*.pdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.log
-*.scc
-
-ipch/
-*.aps
-*.ncb
-*.opensdf
-*.sdf
-*.cachefile
-
-*.psess
-*.vsp
-*.vspx
-
-*.gpState
-
-_ReSharper*/
-*.[Rr]e[Ss]harper
-
-_TeamCity*
-
-*.dotCover
-
-*.ncrunch*
-.*crunch*.local.xml
-
-[Ee]xpress/
-
-*.Publish.xml
-*.pubxml.user
-
-project.lock.json
-
-csx
-*.build.csdef
-
-# Others
-sql/
-*.Cache
-ClientBin/
-[Ss]tyle[Cc]op.*
-~$*
-*~
-*.dbmdl
-*.[Pp]ublish.xml
-*.pfx
-*.publishsettings
-
-# SQL Server files
-App_Data/*.mdf
-App_Data/*.ldf
-
-#############
-## Windows detritus
-#############
-
-# Windows image file caches
-Thumbs.db
-ehthumbs.db
-
-# Folder config file
-Desktop.ini
-
-# Recycle Bin used on file shares
-$RECYCLE.BIN/
-
-# Mac crap
-.DS_Store
-
-#############
-## Ignore Logs
-#############
-
-logs/
-
-src/Miningcore/config.json
-src/Miningcore/config2.json
-/src/Miningcore/coins2.json
-
-!**/lib/**/*
-
-# Created by https://www.toptal.com/developers/gitignore/api/rider
-# Edit at https://www.toptal.com/developers/gitignore?templates=rider
-
-### Rider ###
-# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
-# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
-
-# User-specific stuff
-.idea/**/workspace.xml
-.idea/**/tasks.xml
-.idea/**/usage.statistics.xml
-.idea/**/dictionaries
-.idea/**/shelf
-
-# AWS User-specific
-.idea/**/aws.xml
-
-# Generated files
-.idea/**/contentModel.xml
-
-# Sensitive or high-churn files
-.idea/**/dataSources/
-.idea/**/dataSources.ids
-.idea/**/dataSources.local.xml
-.idea/**/sqlDataSources.xml
-.idea/**/dynamic.xml
-.idea/**/uiDesigner.xml
-.idea/**/dbnavigator.xml
-
-# Gradle
-.idea/**/gradle.xml
-.idea/**/libraries
-
-# Gradle and Maven with auto-import
-# When using Gradle or Maven with auto-import, you should exclude module files,
-# since they will be recreated, and may cause churn. Uncomment if using
-# auto-import.
-# .idea/artifacts
-# .idea/compiler.xml
-# .idea/jarRepositories.xml
-# .idea/modules.xml
-# .idea/*.iml
-# .idea/modules
-# *.iml
-# *.ipr
-
-# CMake
-cmake-build-*/
-
-# Mongo Explorer plugin
-.idea/**/mongoSettings.xml
-
-# File-based project format
-*.iws
-
-# IntelliJ
-out/
-
-# mpeltonen/sbt-idea plugin
-.idea_modules/
-
-# JIRA plugin
-atlassian-ide-plugin.xml
-
-# Cursive Clojure plugin
-.idea/replstate.xml
-
-# SonarLint plugin
-.idea/sonarlint/
-
-# Crashlytics plugin (for Android Studio and IntelliJ)
-com_crashlytics_export_strings.xml
-crashlytics.properties
-crashlytics-build.properties
-fabric.properties
-
-# Editor-based Rest Client
-.idea/httpRequests
-
-# Android studio 3.1+ serialized cache file
-.idea/caches/build_file_checksums.ser
-
-# End of https://www.toptal.com/developers/gitignore/api/rider
-.idea
-
-.fake
-/nbproject/
-/*/*/*/*.a
-/*/*/*/*/*/*/*/*.o
-/*/*/*/*/*/*/*/*/*.o
-/*/*/*/*/*/*/*/*/*/*.o
-/*/*/*/*/*/*/*/*/*/*.o
diff --git a/Dockerfile b/Dockerfile
index 0075e3412e..2803545634 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0-jammy as BUILDER
WORKDIR /app
RUN apt-get update && \
- apt-get -y install cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libzmq3-dev golang-go libgmp-dev
+ apt-get -y install cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libzmq3-dev golang-go libgmp-dev libc++-dev zlib1g-dev
COPY . .
WORKDIR /app/src/Miningcore
RUN dotnet publish -c Release --framework net6.0 -o ../../build
diff --git a/README.md b/README.md
index 5b67d43bcd..0216dbc850 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
[](https://ci.appveyor.com/project/oliverw/miningcore)
-[](https://github.com/blackmennewstyle/miningcore/actions/workflows/dotnet.yml)
+[](https://github.com/blackmennewstyle/miningcore/actions/workflows/dotnet.yml)
[]()
@@ -83,14 +83,14 @@ cd miningcore
Then build using Docker:
```console
-docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:6.0 /bin/bash -c 'apt update && apt install cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5n libzmq3-dev golang-go libgmp-dev -y --no-install-recommends && cd src/Miningcore && dotnet publish -c Release --framework net6.0 -o /app/build/'
+docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:6.0 /bin/bash -c 'apt update && apt install cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libzmq3-dev golang-go libgmp-dev libc++-dev zlib1g-dev -y --no-install-recommends && cd src/Miningcore && dotnet publish -c Release --framework net6.0 -o /app/build/'
```
It will use a Linux container, you will build a Linux executable that will not run on Windows or macOS. You can use a runtime argument (-r) to specify the type of assets that you want to publish (if they don't match the SDK container). The following examples assume you want assets that match your host operating system, and use runtime arguments to ensure that.
For macOS:
```console
-docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:6.0 /bin/bash -c 'apt update && apt install cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5n libzmq3-dev golang-go libgmp-dev -y --no-install-recommends && cd src/Miningcore && dotnet publish -c Release --framework net6.0 -o /app/build/ -r osx-x64 --self-contained false'
+docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:6.0 /bin/bash -c 'apt update && apt install cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libzmq3-dev golang-go libgmp-dev libc++-dev zlib1g-dev -y --no-install-recommends && cd src/Miningcore && dotnet publish -c Release --framework net6.0 -o /app/build/ -r osx-x64 --self-contained false'
```
### Building and Running Miningcore from a container
@@ -121,7 +121,7 @@ docker run -d \
For Windows using Linux container:
```console
-docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:6.0 /bin/bash -c 'apt update && apt install cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5n libzmq3-dev golang-go libgmp-dev -y --no-install-recommends && cd src/Miningcore && dotnet publish -c Release --framework net6.0 -o /app/build/ -r win-x64 --self-contained false'
+docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:6.0 /bin/bash -c 'apt update && apt install cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libzmq3-dev golang-go libgmp-dev libc++-dev zlib1g-dev -y --no-install-recommends && cd src/Miningcore && dotnet publish -c Release --framework net6.0 -o /app/build/ -r win-x64 --self-contained false'
```
To delete used images and containers you can run after all:
@@ -239,4 +239,8 @@ To support this project you can become a [sponsor]( https://github.com/sponsors/
* CCX: `ccx7S4B3gBeH1SGWCfqZp3NM7Vavg7H3S8ovJn8fU4bwC4vU7ChWfHtbNzifhrpbJ74bMDxj4KZFTcznTfsucCEg1Kgv7zbNgs`
* FIRO: `a5AsoTSkfPHQ3SUmR6binG1XW7oQQoFNU1`
* ERGO: `9gYyuZzaSw3TiCtUkSRuS3XVDUv41EFs3dtNCFGqiEwHqpb7gkF`
+* WART: `7795fc0fe93e7e4e232a212f00bdc8885c580a5666d39a0d`
* XMR: `483zaHtMRfM7rw1dXgebhWaRR8QLgAF6w4BomAV319FVVHfdbYTLVuBRc4pQgRAnRpfy6CXvvwngK4Lo3mRKE29RRx3Jb5c`
+* XEL: `xel:ajnsfv065qusndt0hfsngecrnf5690drmqmc0uq0etlx8zjlcyzqq2slgvt`
+* CTXC: `0xbb60200d5151a4a0f9a75014e04cf61a0a9f0daf`
+* ZANO: `ZxDKT1aqiEXPA5cDADtYEfMR1oXsRd68bby4nzUvVmnjHzzrfvjwhNdQ9yiWNeGutzg9LZdwsbP2FGB1gNpZXiYY1fCfpw33c`
diff --git a/build-debian-11.sh b/build-debian-11.sh
index 87b3f46d5e..16e767839c 100755
--- a/build-debian-11.sh
+++ b/build-debian-11.sh
@@ -5,13 +5,13 @@ sudo apt-get update; \
sudo apt-get -y install wget
# add dotnet repo
-sudo wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
+wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
# install dev-dependencies
sudo apt-get update; \
- sudo apt-get -y install dotnet-sdk-6.0 git cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5-dev libgmp-dev
+ sudo apt-get -y install dotnet-sdk-6.0 git cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5-dev libgmp-dev libc++-dev zlib1g-dev
(cd src/Miningcore && \
BUILDIR=${1:-../../build} && \
diff --git a/build-debian-12.sh b/build-debian-12.sh
index 4ed7c3e29a..ea0640cd97 100755
--- a/build-debian-12.sh
+++ b/build-debian-12.sh
@@ -5,13 +5,13 @@ sudo apt-get update; \
sudo apt-get -y install wget
# add dotnet repo
-sudo wget https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
+wget https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
# install dev-dependencies
sudo apt-get update; \
- sudo apt-get -y install dotnet-sdk-6.0 git cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5-dev libgmp-dev
+ sudo apt-get -y install dotnet-sdk-6.0 git cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5-dev libgmp-dev libc++-dev zlib1g-dev
(cd src/Miningcore && \
BUILDIR=${1:-../../build} && \
diff --git a/build-ubuntu-20.04.sh b/build-ubuntu-20.04.sh
index e0d5145925..279d15a99d 100755
--- a/build-ubuntu-20.04.sh
+++ b/build-ubuntu-20.04.sh
@@ -11,7 +11,7 @@ rm packages-microsoft-prod.deb
# install dev-dependencies
sudo apt-get update; \
- sudo apt-get -y install dotnet-sdk-6.0 git cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libgmp-dev
+ sudo apt-get -y install dotnet-sdk-6.0 git cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libgmp-dev libc++-dev zlib1g-dev
(cd src/Miningcore && \
BUILDIR=${1:-../../build} && \
diff --git a/build-ubuntu-21.04.sh b/build-ubuntu-21.04.sh
index c377bfff59..ebd59a4573 100755
--- a/build-ubuntu-21.04.sh
+++ b/build-ubuntu-21.04.sh
@@ -11,7 +11,7 @@ rm packages-microsoft-prod.deb
# install dev-dependencies
sudo apt-get update; \
- sudo apt-get -y install dotnet-sdk-6.0 git cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libgmp-dev
+ sudo apt-get -y install dotnet-sdk-6.0 git cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libgmp-dev libc++-dev zlib1g-dev
(cd src/Miningcore && \
BUILDIR=${1:-../../build} && \
diff --git a/build-ubuntu-22.04.sh b/build-ubuntu-22.04.sh
index b77ad6e3c5..aab6aadfdb 100755
--- a/build-ubuntu-22.04.sh
+++ b/build-ubuntu-22.04.sh
@@ -4,7 +4,7 @@
# install dev-dependencies
sudo apt-get update; \
- sudo apt-get -y install dotnet6 git cmake ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libgmp-dev
+ sudo apt-get -y install dotnet-sdk-6.0 git cmake clang ninja-build build-essential libssl-dev pkg-config libboost-all-dev libsodium-dev libzmq5 libgmp-dev libc++-dev zlib1g-dev
(cd src/Miningcore && \
BUILDIR=${1:-../../build} && \
diff --git a/build-windows.bat b/build-windows.bat
index 87268cf1a6..f666e4608c 100644
--- a/build-windows.bat
+++ b/build-windows.bat
@@ -1,3 +1,3 @@
-@echo off
-cd src\Miningcore
-dotnet publish -c Release --framework net6.0 -o ../../build
+@echo off
+cd src\Miningcore
+dotnet publish -c Release --framework net6.0 -o ../../build
diff --git a/examples/alephium_pool.json b/examples/alephium_pool.json
index 394cc70bfb..04e8aeaade 100644
--- a/examples/alephium_pool.json
+++ b/examples/alephium_pool.json
@@ -74,6 +74,7 @@
"percentage": 1
}
],
+ "enableAsicBoost": true,
"clientConnectionTimeout": 600,
"socketJobMessageBufferSize": 16384,
"banning": {
diff --git a/examples/arqma_pool.json b/examples/arqma_pool.json
deleted file mode 100644
index 49ad9cecc0..0000000000
--- a/examples/arqma_pool.json
+++ /dev/null
@@ -1,148 +0,0 @@
-{
- "logging":
- {
- "level": "info",
- "enableConsoleLog": true,
- "enableConsoleColors": true,
- "logFile": "",
- "apiLogFile": "",
- "logBaseDirectory": "",
- "perPoolLogFile": false
- },
- "banning":
- {
- "manager": "Integrated",
- "banOnJunkReceive": true,
- "banOnInvalidShares": false
- },
- "notifications":
- {
- "enabled": false,
- "email":
- {
- "host": "smtp.example.com",
- "port": 587,
- "user": "user",
- "password": "password",
- "fromAddress": "info@yourpool.org",
- "fromName": "support"
- },
- "admin":
- {
- "enabled": false,
- "emailAddress": "user@example.com",
- "notifyBlockFound": true
- }
- },
- "persistence":
- {
- "postgres":
- {
- "host": "127.0.0.1",
- "port": 5432,
- "user": "miningcore",
- "password": "password",
- "database": "miningcore"
- }
- },
- "paymentProcessing":
- {
- "enabled": true,
- "interval": 600,
- "shareRecoveryFile": "recovered-shares.txt"
- },
- "api":
- {
- "enabled": true,
- "listenAddress": "*",
- "port": 4000,
- "metricsIpWhitelist": [],
- "rateLimiting":
- {
- "disabled": true,
- "rules":
- [
- {
- "Endpoint": "*",
- "Period": "1s",
- "Limit": 5
- }
- ],
- "ipWhitelist":
- [
- ""
- ]
- }
- },
- "pools":
- [
- {
- "id": "arqma",
- "enabled": true,
- "coin": "zephyr",
- "randomXRealm": "arqma",
- "address": "ar2dDEehonEYXkJkXyZGNxAZhU8vFKmrtPKgebmKDpMvYz8YPjiRztfR5WMWpH8u6MCqsVpqnJ5bh5VVgP2Sw1Xh2iWsqC4ro",
- "rewardRecipients":
- [
- {
- "address": "ar2dDEehonEYXkJkXyZGNxAZhU8vFKmrtPKgebmKDpMvYz8YPjiRztfR5WMWpH8u6MCqsVpqnJ5bh5VVgP2Sw1Xh2iWsqC4ro",
- "percentage": 1
- }
- ],
- "blockRefreshInterval": 500,
- "clientConnectionTimeout": 600,
- "banning":
- {
- "enabled": true,
- "time": 600,
- "invalidPercent": 50,
- "checkThreshold": 50
- },
- "ports":
- {
- "6360":
- {
- "listenAddress": "0.0.0.0",
- "difficulty": 50000,
- "name": "CPU Mining",
- "varDiff":
- {
- "minDiff": 15000,
- "maxDiff": null,
- "targetTime": 15,
- "retargetTime": 90,
- "variancePercent": 30
- }
- }
- },
- "daemons":
- [
- {
- "host": "127.0.0.1",
- "port": 19994,
- "user": "user",
- "password": "password"
- },
- {
- "host": "127.0.0.1",
- "port": 19995,
- "user": "user",
- "password": "password",
- "category": "wallet"
- }
- ],
- "paymentProcessing":
- {
- "enabled": true,
- "minimumPayment": 0.25,
- "minimumPaymentToPaymentId": 1.0,
- "transferSplit": true,
- "payoutScheme": "PPLNS",
- "payoutSchemeConfig":
- {
- "factor": 0.5
- }
- }
- }
- ]
-}
diff --git a/examples/cortex_pool.json b/examples/cortex_pool.json
new file mode 100644
index 0000000000..0b5e48bb11
--- /dev/null
+++ b/examples/cortex_pool.json
@@ -0,0 +1,125 @@
+{
+ "logging": {
+ "level": "info",
+ "enableConsoleLog": true,
+ "enableConsoleColors": true,
+ "logFile": "",
+ "apiLogFile": "",
+ "logBaseDirectory": "",
+ "perPoolLogFile": false
+ },
+ "banning": {
+ "manager": "Integrated",
+ "banOnJunkReceive": false,
+ "banOnInvalidShares": false
+ },
+ "notifications": {
+ "enabled": false,
+ "email": {
+ "host": "smtp.example.com",
+ "port": 587,
+ "user": "user",
+ "password": "password",
+ "fromAddress": "info@yourpool.org",
+ "fromName": "pool support"
+ },
+ "admin": {
+ "enabled": false,
+ "emailAddress": "user@example.com",
+ "notifyBlockFound": true
+ }
+ },
+ "persistence": {
+ "postgres": {
+ "host": "127.0.0.1",
+ "port": 5432,
+ "user": "miningcore",
+ "password": "password",
+ "database": "miningcore"
+ }
+ },
+ "paymentProcessing": {
+ "enabled": true,
+ "interval": 100,
+ "shareRecoveryFile": "recovered-shares.txt"
+ },
+ "api": {
+ "enabled": true,
+ "listenAddress": "*",
+ "port": 4000,
+ "metricsIpWhitelist": [],
+ "rateLimiting": {
+ "disabled": true,
+ "rules": [
+ {
+ "Endpoint": "*",
+ "Period": "1s",
+ "Limit": 5
+ }
+ ],
+ "ipWhitelist": [
+ ""
+ ]
+ }
+ },
+ "clusterName": "eu1",
+ "pools": [
+ {
+ "id": "ctxc1",
+ "enabled": true,
+ "coin": "cortex",
+ "address": "0x6dd47dff7cf7daae47331f7f3556c0a31ab2511b",
+ "rewardRecipients": [
+ {
+ "type": "op",
+ "address": "0x6dd47dff7cf7daae47331f7f3556c0a31ab2511b",
+ "percentage": 1.0
+ }
+ ],
+ "blockRefreshInterval": 120,
+ "clientConnectionTimeout": 600,
+ "banning": {
+ "enabled": true,
+ "time": 600,
+ "invalidPercent": 50,
+ "checkThreshold": 50
+ },
+ "ports": {
+ "4073": {
+ "name": "GPU-SMALL",
+ "listenAddress": "*",
+ "difficulty": 0.0000000008128,
+ "varDiff": {
+ "minDiff": 0.0000000008128,
+ "maxDiff": null,
+ "targetTime": 15,
+ "retargetTime": 90,
+ "variancePercent": 30
+ }
+ }
+ },
+ "chainTypeOverride": "Cortex",
+ "daemons": [
+ {
+ "host": "127.0.0.1",
+ "port": 8545,
+ "user": "",
+ "password": ""
+ }
+ ],
+ "paymentProcessing": {
+ "enabled": true,
+ "minimumPayment": 1,
+ "payoutScheme": "PPLNS",
+ "payoutSchemeConfig": {
+ "factor": 0.5
+ },
+ "gas": 21000,
+ "maxFeePerGas": 50000000000,
+ "BlockSearchOffset": 100,
+ "keepUncles": false,
+ "keepTransactionFees": false
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/handshake_pool.json b/examples/handshake_pool.json
index d12d163a85..5d7e9d4120 100644
--- a/examples/handshake_pool.json
+++ b/examples/handshake_pool.json
@@ -75,6 +75,7 @@
"percentage": 1
}
],
+ "enableAsicBoost": true,
"blockRefreshInterval": 250,
"jobRebroadcastTimeout": 0,
"clientConnectionTimeout": 600,
diff --git a/examples/kaspa_pool.json b/examples/kaspa_pool.json
index aa9ff89253..f53f361f18 100644
--- a/examples/kaspa_pool.json
+++ b/examples/kaspa_pool.json
@@ -75,6 +75,7 @@
}
],
"protobufWalletRpcServiceName": "kaspawalletd.kaspawalletd",
+ "enableAsicBoost": true,
"clientConnectionTimeout": 600,
"banning": {
"enabled": true,
@@ -130,6 +131,8 @@
"enabled": true,
"walletPassword": "",
"minimumPayment": 1,
+ "versionEnablingMaxFee": "v0.12.18-rc5",
+ "maxFee": 200000,
"payoutScheme": "PPLNS",
"payoutSchemeConfig": {
"factor": 0.5
diff --git a/examples/veruscoin_pool.json b/examples/veruscoin_pool.json
index 295a2e1423..5ca7534e1e 100644
--- a/examples/veruscoin_pool.json
+++ b/examples/veruscoin_pool.json
@@ -96,10 +96,10 @@
"ports": {
"3092": {
"listenAddress": "0.0.0.0",
- "difficulty": 256,
+ "difficulty": 25600000,
"varDiff": {
- "minDiff": 256,
- "maxDiff": 1048576000,
+ "minDiff": 25600000,
+ "maxDiff": null,
"targetTime": 15,
"retargetTime": 90,
"variancePercent": 30,
@@ -108,13 +108,13 @@
},
"3093": {
"listenAddress": "0.0.0.0",
- "difficulty": 256,
+ "difficulty": 25600000,
"tls": true,
"tlsPfxFile": "",
"tlsPfxPassword": "password",
"varDiff": {
- "minDiff": 256,
- "maxDiff": 1048576000,
+ "minDiff": 25600000,
+ "maxDiff": null,
"targetTime": 15,
"retargetTime": 90,
"variancePercent": 30,
diff --git a/examples/warthog_pool.json b/examples/warthog_pool.json
new file mode 100644
index 0000000000..ebe22690f4
--- /dev/null
+++ b/examples/warthog_pool.json
@@ -0,0 +1,119 @@
+{
+ "logging": {
+ "level": "info",
+ "enableConsoleLog": true,
+ "enableConsoleColors": true,
+ "logFile": "",
+ "apiLogFile": "",
+ "logBaseDirectory": "",
+ "perPoolLogFile": false
+ },
+ "banning": {
+ "manager": "Integrated",
+ "banOnJunkReceive": false,
+ "banOnInvalidShares": false
+ },
+ "notifications": {
+ "enabled": false,
+ "email": {
+ "host": "smtp.example.com",
+ "port": 587,
+ "user": "user",
+ "password": "password",
+ "fromAddress": "info@yourpool.org",
+ "fromName": "support"
+ },
+ "admin": {
+ "enabled": false,
+ "emailAddress": "user@example.com",
+ "notifyBlockFound": true
+ }
+ },
+ "persistence": {
+ "postgres": {
+ "host": "127.0.0.1",
+ "port": 5432,
+ "user": "miningcore",
+ "password": "password",
+ "database": "miningcore"
+ }
+ },
+ "paymentProcessing": {
+ "enabled": true,
+ "interval": 600,
+ "shareRecoveryFile": "recovered-shares.txt"
+ },
+ "api": {
+ "enabled": true,
+ "listenAddress": "*",
+ "port": 4000,
+ "metricsIpWhitelist": [],
+ "rateLimiting": {
+ "disabled": true,
+ "rules": [
+ {
+ "Endpoint": "*",
+ "Period": "1s",
+ "Limit": 5
+ }
+ ],
+ "ipWhitelist": [
+ ""
+ ]
+ }
+ },
+ "pools": [{
+ "id": "wart1",
+ "enabled": true,
+ "coin": "warthog",
+ "address": "5d896758ee0ade5f09fa93d012783beddd82a6ff322f2073",
+ "rewardRecipients": [
+ {
+ "type": "op",
+ "address": "5d896758ee0ade5f09fa93d012783beddd82a6ff322f2073",
+ "percentage": 1.0
+ }
+ ],
+ "blockRefreshInterval": 250,
+ "jobRebroadcastTimeout": 0,
+ "clientConnectionTimeout": 600,
+ "banning": {
+ "enabled": true,
+ "time": 600,
+ "invalidPercent": 50,
+ "checkThreshold": 50
+ },
+ "ports": {
+ "4073": {
+ "name": "GPU-SMALL",
+ "listenAddress": "*",
+ "difficulty": 512000000,
+ "varDiff": {
+ "minDiff": 256000000,
+ "maxDiff": null,
+ "targetTime": 15,
+ "retargetTime": 90,
+ "variancePercent": 100,
+ "maxDelta": 512
+ }
+ }
+ },
+ "daemons": [
+ {
+ "host": "127.0.0.1",
+ "port": 3000,
+ "user": "",
+ "password": ""
+ }
+ ],
+ "paymentProcessing": {
+ "walletPrivateKey": "",
+ "enabled": true,
+ "minimumPayment": 1,
+ "payoutScheme": "PPLNS",
+ "payoutSchemeConfig": {
+ "factor": 0.5
+ }
+ }
+ }]
+}
\ No newline at end of file
diff --git a/examples/xelis_pool.json b/examples/xelis_pool.json
new file mode 100644
index 0000000000..85cb26b0e1
--- /dev/null
+++ b/examples/xelis_pool.json
@@ -0,0 +1,125 @@
+{
+ "logging": {
+ "level": "info",
+ "enableConsoleLog": true,
+ "enableConsoleColors": true,
+ "logFile": "",
+ "apiLogFile": "",
+ "logBaseDirectory": "",
+ "perPoolLogFile": false
+ },
+ "banning": {
+ "manager": "Integrated",
+ "banOnJunkReceive": false,
+ "banOnInvalidShares": false
+ },
+ "notifications": {
+ "enabled": false,
+ "email": {
+ "host": "smtp.example.com",
+ "port": 587,
+ "user": "user",
+ "password": "password",
+ "fromAddress": "info@yourpool.org",
+ "fromName": "support"
+ },
+ "admin": {
+ "enabled": false,
+ "emailAddress": "user@example.com",
+ "notifyBlockFound": true
+ }
+ },
+ "persistence": {
+ "postgres": {
+ "host": "127.0.0.1",
+ "port": 5432,
+ "user": "miningcore",
+ "password": "password",
+ "database": "miningcore"
+ }
+ },
+ "paymentProcessing": {
+ "enabled": true,
+ "interval": 600,
+ "shareRecoveryFile": "recovered-shares.txt"
+ },
+ "api": {
+ "enabled": true,
+ "listenAddress": "*",
+ "port": 4000,
+ "metricsIpWhitelist": [],
+ "rateLimiting": {
+ "disabled": true,
+ "rules": [
+ {
+ "Endpoint": "*",
+ "Period": "1s",
+ "Limit": 5
+ }
+ ],
+ "ipWhitelist": [
+ ""
+ ]
+ }
+ },
+ "pools": [{
+ "id": "xel1",
+ "enabled": true,
+ "coin": "xelis",
+ "address": "xel:ajnsfv065qusndt0hfsngecrnf5690drmqmc0uq0etlx8zjlcyzqq2slgvt",
+ "rewardRecipients": [
+ {
+ "type": "op",
+ "address": "xel:ajnsfv065qusndt0hfsngecrnf5690drmqmc0uq0etlx8zjlcyzqq2slgvt",
+ "percentage": 1.0
+ }
+ ],
+ "blockRefreshInterval": 250,
+ "jobRebroadcastTimeout": 0,
+ "clientConnectionTimeout": 600,
+ "banning": {
+ "enabled": true,
+ "time": 600,
+ "invalidPercent": 50,
+ "checkThreshold": 50
+ },
+ "ports": {
+ "4073": {
+ "name": "GPU-SMALL",
+ "listenAddress": "*",
+ "difficulty": 1024000,
+ "varDiff": {
+ "minDiff": 512000,
+ "maxDiff": null,
+ "targetTime": 15,
+ "retargetTime": 90,
+ "variancePercent": 100,
+ "maxDelta": 512
+ }
+ }
+ },
+ "daemons": [
+ {
+ "host": "127.0.0.1",
+ "port": 8080,
+ "user": null,
+ "password": null
+ },
+ {
+ "host": "127.0.0.1",
+ "port": 8081,
+ "user": "",
+ "password": "",
+ "category": "wallet"
+ }
+ ],
+ "paymentProcessing": {
+ "enabled": true,
+ "minimumPayment": 1,
+ "payoutScheme": "PPLNS",
+ "payoutSchemeConfig": {
+ "factor": 0.5
+ }
+ }
+ }]
+}
\ No newline at end of file
diff --git a/examples/zano_pool.json b/examples/zano_pool.json
new file mode 100644
index 0000000000..954266a3af
--- /dev/null
+++ b/examples/zano_pool.json
@@ -0,0 +1,141 @@
+{
+ "logging": {
+ "level": "info",
+ "enableConsoleLog": true,
+ "enableConsoleColors": true,
+ "logFile": "",
+ "apiLogFile": "",
+ "logBaseDirectory": "",
+ "perPoolLogFile": false
+ },
+ "banning": {
+ "manager": "Integrated",
+ "banOnJunkReceive": true,
+ "banOnInvalidShares": false
+ },
+ "notifications": {
+ "enabled": false,
+ "email": {
+ "host": "smtp.example.com",
+ "port": 587,
+ "user": "user",
+ "password": "password",
+ "fromAddress": "info@yourpool.org",
+ "fromName": "pool support"
+ },
+ "admin": {
+ "enabled": false,
+ "emailAddress": "user@example.com",
+ "notifyBlockFound": true
+ }
+ },
+ "persistence": {
+ "postgres": {
+ "host": "127.0.0.1",
+ "port": 5432,
+ "user": "miningcore",
+ "password": "password",
+ "database": "miningcore"
+ }
+ },
+ "paymentProcessing": {
+ "enabled": true,
+ "interval": 600,
+ "shareRecoveryFile": "recovered-shares.txt"
+ },
+ "api": {
+ "enabled": true,
+ "listenAddress": "*",
+ "port": 4000,
+ "metricsIpWhitelist": [],
+ "rateLimiting": {
+ "disabled": true,
+ "rules": [
+ {
+ "Endpoint": "*",
+ "Period": "1s",
+ "Limit": 5
+ }
+ ],
+ "ipWhitelist": [
+ ""
+ ]
+ }
+ },
+ "pools": [
+ {
+ "id": "zano1",
+ "enabled": true,
+ "coin": "zano",
+ "address": "ZxDKT1aqiEXPA5cDADtYEfMR1oXsRd68bby4nzUvVmnjHzzrfvjwhNdQ9yiWNeGutzg9LZdwsbP2FGB1gNpZXiYY1fCfpw33c",
+ "rewardRecipients": [
+ {
+ "type": "op",
+ "address": "ZxDKT1aqiEXPA5cDADtYEfMR1oXsRd68bby4nzUvVmnjHzzrfvjwhNdQ9yiWNeGutzg9LZdwsbP2FGB1gNpZXiYY1fCfpw33c",
+ "percentage": 0.1
+ }
+ ],
+ "blockRefreshInterval": 250,
+ "jobRebroadcastTimeout": 3,
+ "clientConnectionTimeout": 600,
+ "banning": {
+ "enabled": true,
+ "time": 600,
+ "invalidPercent": 50,
+ "checkThreshold": 50
+ },
+ "ports": {
+ "3364": {
+ "listenAddress": "0.0.0.0",
+ "difficulty": 250000000,
+ "varDiff": {
+ "minDiff": 250000000,
+ "maxDiff": null,
+ "targetTime": 15,
+ "retargetTime": 90,
+ "variancePercent": 30,
+ "maxDelta": 500
+ }
+ },
+ "3365": {
+ "listenAddress": "0.0.0.0",
+ "difficulty": 250000000,
+ "tls": true,
+ "tlsPfxFile": "",
+ "tlsPfxPassword": "password",
+ "varDiff": {
+ "minDiff": 250000000,
+ "maxDiff": null,
+ "targetTime": 15,
+ "retargetTime": 90,
+ "variancePercent": 30,
+ "maxDelta": 500
+ }
+ }
+ },
+ "daemons": [
+ {
+ "host": "127.0.0.1",
+ "port": 11211,
+ "user": null,
+ "password": null
+ },
+ {
+ "host": "127.0.0.1",
+ "port": 11212,
+ "user": null,
+ "password": null,
+ "category": "wallet"
+ }
+ ],
+ "paymentProcessing": {
+ "enabled": true,
+ "minimumPayment": 0.1,
+ "payoutScheme": "PPLNS",
+ "payoutSchemeConfig": {
+ "factor": 0.5
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/Miningcore.Tests/Miningcore.Tests.csproj b/src/Miningcore.Tests/Miningcore.Tests.csproj
index 7ea90a8840..f552aeab8d 100644
--- a/src/Miningcore.Tests/Miningcore.Tests.csproj
+++ b/src/Miningcore.Tests/Miningcore.Tests.csproj
@@ -37,8 +37,8 @@
-
-
+
+
diff --git a/src/Miningcore/Api/Controllers/AdminApiController.cs b/src/Miningcore/Api/Controllers/AdminApiController.cs
index aa54e54d79..24fc289b1e 100644
--- a/src/Miningcore/Api/Controllers/AdminApiController.cs
+++ b/src/Miningcore/Api/Controllers/AdminApiController.cs
@@ -127,7 +127,7 @@ public ActionResult EnablePoolPaymentProcessing(string poolId)
poolInstance.Config.PaymentProcessing.Enabled = true;
logger.Info(()=> $"Enabled payment processing for pool {poolId}");
- return "Payment Enabled Successfully";
+ return "Ok";
}
[HttpGet("payment/processing/{poolId}/disable")]
@@ -142,40 +142,8 @@ public ActionResult DisablePoolPaymentProcessing(string poolId)
poolInstance.Config.PaymentProcessing.Enabled = false;
logger.Info(()=> $"Disabled payment processing for pool {poolId}");
- return "Payment Disable Successfully";
- }
-
- // testing pool id enable / disable on admin api start here
- [HttpGet("{poolId}/enable")]
- public ActionResult EnablePoolId(string poolId)
- {
- if (string.IsNullOrEmpty(poolId))
- throw new ApiException("Missing pool ID", HttpStatusCode.BadRequest);
-
- pools.TryGetValue(poolId, out var poolInstance);
- if(poolInstance == null)
- return "-1";
-
- poolInstance.Config.Enabled = true;
- logger.Info(()=> $"Enabled pool ID{poolId}");
- return "{poolId} Enabled Successfully";
- }
-
- [HttpGet("{poolId}/disable")]
- public ActionResult DisablePoolId(string poolId)
- {
- if (string.IsNullOrEmpty(poolId))
- throw new ApiException("Missing pool ID", HttpStatusCode.BadRequest);
-
- pools.TryGetValue(poolId, out var poolInstance);
- if(poolInstance == null)
- return "-1";
-
- poolInstance.Config.Enabled = false;
- logger.Info(()=> $"Disabled pool Id{poolId}");
- return "{poolId} Disabled Successfully";
+ return "Ok";
}
- // testing pool id enable / disable on admin api end here
[HttpGet("stats/gc")]
public ActionResult GetGcStats()
diff --git a/src/Miningcore/Api/Controllers/PoolApiController.cs b/src/Miningcore/Api/Controllers/PoolApiController.cs
index d491e9ec4f..bdaa34e8cc 100644
--- a/src/Miningcore/Api/Controllers/PoolApiController.cs
+++ b/src/Miningcore/Api/Controllers/PoolApiController.cs
@@ -50,61 +50,86 @@ public PoolApiController(IComponentContext ctx, IActionDescriptorCollectionProvi
#region Actions
[HttpGet]
- public async Task Get(CancellationToken ct, [FromQuery] uint topMinersRange = 24)
+ public async Task Get(CancellationToken ct,
+ [FromServices] CoinMarketCap.CoinMarketCapService service,
+ [FromQuery] uint topMinersRange = 24)
{
- var response = new GetPoolsResponse
+ try
{
- Pools = await Task.WhenAll(clusterConfig.Pools.Where(x => x.Enabled).Select(async config =>
+ var response = new GetPoolsResponse
{
- // load stats
- var stats = await cf.Run(con => statsRepo.GetLastPoolStatsAsync(con, config.Id, ct));
+ Pools = await Task.WhenAll(clusterConfig.Pools.Where(x => x.Enabled).Select(async config =>
+ {
+ // load stats
+ var stats = await cf.Run(con => statsRepo.GetLastPoolStatsAsync(con, config.Id, ct));
+
+ // get pool
+ pools.TryGetValue(config.Id, out var pool);
+
+ // map
+ var result = config.ToPoolInfo(mapper, stats, pool);
+
+ // enrich
+ result.TotalPaid = await cf.Run(con => statsRepo.GetTotalPoolPaymentsAsync(con, config.Id, ct));
+ result.TotalBlocks = await cf.Run(con => blocksRepo.GetPoolBlockCountAsync(con, config.Id, ct));
+ result.TotalConfirmedBlocks = await cf.Run(con => blocksRepo.GetTotalConfirmedBlocksAsync(con, config.Id, ct));
+ result.TotalPendingBlocks = await cf.Run(con => blocksRepo.GetTotalPendingBlocksAsync(con, config.Id, ct));
+ // get reward of the last confirmed block and set BlockReward
+ result.BlockReward = await cf.Run(con => blocksRepo.GetLastConfirmedBlockRewardAsync(con, config.Id, ct));
+ var lastBlockTime = await cf.Run(con => blocksRepo.GetLastPoolBlockTimeAsync(con, config.Id, ct));
+ result.LastPoolBlockTime = lastBlockTime;
+
+ if(lastBlockTime.HasValue)
+ {
+ var startTime = lastBlockTime.Value;
+ var poolEffort = await cf.Run(con => shareRepo.GetEffortBetweenCreatedAsync(con, config.Id, pool.ShareMultiplier, startTime, clock.Now, ct));
+ if(poolEffort.HasValue)
+ result.PoolEffort = poolEffort.Value;
+ }
- // get pool
- pools.TryGetValue(config.Id, out var pool);
+ var from = clock.Now.AddHours(-topMinersRange);
- // map
- var result = config.ToPoolInfo(mapper, stats, pool);
+ var minersByHashrate = await cf.Run(con => statsRepo.PagePoolMinersByHashrateAsync(con, config.Id, from, 0, 15, ct));
- // enrich
- result.TotalPaid = await cf.Run(con => statsRepo.GetTotalPoolPaymentsAsync(con, config.Id, ct));
- result.TotalBlocks = await cf.Run(con => blocksRepo.GetPoolBlockCountAsync(con, config.Id, ct));
- var lastBlockTime = await cf.Run(con => blocksRepo.GetLastPoolBlockTimeAsync(con, config.Id));
- result.LastPoolBlockTime = lastBlockTime;
+ result.TopMiners = minersByHashrate.Select(mapper.Map).ToArray();
- var payoutConfig = config.PaymentProcessing;
- result.PaymentProcessing.PayoutSchemeConfig = payoutConfig?.PayoutSchemeConfig.ToObject();
- // display block finder percentage only if PPLNSBF is activated
- if(payoutConfig?.PayoutScheme != PayoutScheme.PPLNSBF)
- result.PaymentProcessing.PayoutSchemeConfig.BlockFinderPercentage = null;
+ return result;
+ }).ToArray())
+ };
- if(lastBlockTime.HasValue)
+ if(clusterConfig.CoinMarketCapApi.Enabled)
+ {
+ var symbols = string.Join(",", response?.Pools?.Select(Q => Q.Coin.Symbol));
+ if(!string.IsNullOrWhiteSpace(symbols))
{
- var startTime = lastBlockTime.Value;
- var poolEffort = await cf.Run(con => shareRepo.GetEffortBetweenCreatedAsync(con, config.Id, startTime, clock.Now));
-
- //kaspa effort fix start here
- // If the effort is less than 1e-8, multiply it by 4e9
- if(poolEffort.HasValue && poolEffort.Value < 1e-8)
+ var result = await service.GetCryptoQuoteAsync(symbols);
+ foreach(var item in response.Pools)
{
- poolEffort *= 4e9;
+ if(result.Data.TryGetValue(item.Coin.Symbol, out var marketCapData))
+ {
+ var marketData = marketCapData.Where(Q => string.Equals(item.Coin.Symbol, Q.Symbol, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
+ if(marketData?.Quote != null && marketData.Quote.TryGetValue("USD", out var quote) && quote?.Price != null && quote.Price.HasValue)
+ {
+ item.Coin.Price = Math.Round(quote.Price.Value, 4);
+ item.Coin.Logo = marketData.Logo;
+ item.Coin.VolumeChange24H = quote.VolumeChange24H.HasValue ? Math.Round(quote.VolumeChange24H.Value, 4) : "n/a";
+ }
+ else
+ {
+ item.Coin.Price = "n/a";
+ item.Coin.Logo = "n/a";
+ item.Coin.VolumeChange24H = "n/a";
+ }
+ }
}
- //kaspa effort fix end here
-
- if(poolEffort.HasValue)
- result.PoolEffort = poolEffort.Value;
}
-
- var from = clock.Now.AddHours(-topMinersRange);
-
- var minersByHashrate = await cf.Run(con => statsRepo.PagePoolMinersByHashrateAsync(con, config.Id, from, 0, 15, ct));
-
- result.TopMiners = minersByHashrate.Select(mapper.Map).ToArray();
-
- return result;
- }).ToArray())
- };
-
- return response;
+ }
+ return Ok(response);
+ }
+ catch(Exception ex)
+ {
+ return Problem(detail: ex.Message, statusCode: 500, title: "Internal Server Error");
+ }
}
[HttpGet("/api/help")]
@@ -152,28 +177,17 @@ public async Task GetPoolInfoAsync(string poolId, CancellationT
// enrich
response.Pool.TotalPaid = await cf.Run(con => statsRepo.GetTotalPoolPaymentsAsync(con, pool.Id, ct));
response.Pool.TotalBlocks = await cf.Run(con => blocksRepo.GetPoolBlockCountAsync(con, pool.Id, ct));
- var lastBlockTime = await cf.Run(con => blocksRepo.GetLastPoolBlockTimeAsync(con, pool.Id));
+ response.Pool.TotalConfirmedBlocks = await cf.Run(con => blocksRepo.GetTotalConfirmedBlocksAsync(con, pool.Id, ct));
+ response.Pool.TotalPendingBlocks = await cf.Run(con => blocksRepo.GetTotalPendingBlocksAsync(con, pool.Id, ct));
+ // get reward of the last confirmed block and set BlockReward
+ response.Pool.BlockReward = await cf.Run(con => blocksRepo.GetLastConfirmedBlockRewardAsync(con, pool.Id, ct));
+ var lastBlockTime = await cf.Run(con => blocksRepo.GetLastPoolBlockTimeAsync(con, pool.Id, ct));
response.Pool.LastPoolBlockTime = lastBlockTime;
- var payoutConfig = pool.PaymentProcessing;
- response.Pool.PaymentProcessing.PayoutSchemeConfig = payoutConfig?.PayoutSchemeConfig.ToObject();
- // display block finder percentage only if PPLNSBF is activated
- if(payoutConfig?.PayoutScheme != PayoutScheme.PPLNSBF)
- response.Pool.PaymentProcessing.PayoutSchemeConfig.BlockFinderPercentage = null;
-
if(lastBlockTime.HasValue)
{
var startTime = lastBlockTime.Value;
- var poolEffort = await cf.Run(con => shareRepo.GetEffortBetweenCreatedAsync(con, pool.Id, startTime, clock.Now));
-
- //kaspa effort fix start here
- // If the effort is less than 1e-8, multiply it by 4e9
- if(poolEffort.HasValue && poolEffort.Value < 1e-8)
- {
- poolEffort *= 4e9;
- }
- //kaspa effort fix end here
-
+ var poolEffort = await cf.Run(con => shareRepo.GetEffortBetweenCreatedAsync(con, pool.Id, poolInstance.ShareMultiplier, startTime, clock.Now, ct));
if(poolEffort.HasValue)
response.Pool.PoolEffort = poolEffort.Value;
}
@@ -274,20 +288,6 @@ public async Task PagePoolMinersAsync(
block.InfoLink = blockInfobaseUrl.Replace(CoinMetaData.BlockHashPH, block.Hash);
}
}
-
- //kaspa effort fix start here
- // If the effort is less than 1e-8, multiply it by 4e9
- if(block.Effort < 1e-8)
- {
- block.Effort *= 4e9;
- }
-
- // If the minerEffort is less than 1e-8, multiply it by 4e9
- if(block.MinerEffort < 1e-8)
- {
- block.MinerEffort *= 4e9;
- }
- //kaspa effort fix end here
}
return blocks;
@@ -303,7 +303,7 @@ public async Task PagePoolMinersAsync(
var blockStates = state is { Length: > 0 } ?
state :
new[] { BlockStatus.Confirmed, BlockStatus.Pending, BlockStatus.Orphaned };
-
+
uint itemCount = await cf.Run(con => blocksRepo.GetPoolBlockCountAsync(con, poolId, ct));
uint pageCount = (uint) Math.Floor(itemCount / (double) pageSize);
@@ -437,25 +437,22 @@ public async Task PagePoolMinersAsync(
stats.LastPaymentLink = string.Format(baseUrl, statsResult.LastPayment.TransactionConfirmationData);
}
- var lastBlockTime = await cf.Run(con => blocksRepo.GetLastMinerBlockTimeAsync(con, pool.Id, address));
+ var lastBlockTime = await cf.Run(con => blocksRepo.GetLastPoolBlockTimeAsync(con, pool.Id, ct));
if(lastBlockTime.HasValue)
- {
- var startTime = lastBlockTime.Value;
- var minerEffort = await cf.Run(con => shareRepo.GetMinerEffortBetweenCreatedAsync(con, pool.Id, address, startTime, clock.Now));
-
- //kaspa effort fix start here
- // If the effort is less than 1e-8, multiply it by 4e9
- if(minerEffort.HasValue && minerEffort.Value < 1e-8)
- {
- minerEffort *= 4e9;
- }
- //kaspa effort fix end here
-
- if(minerEffort.HasValue)
- stats.MinerEffort = minerEffort.Value;
- }
+ {
+ var startTime = lastBlockTime.Value;
+ var minerEffort = await cf.Run(con => shareRepo.GetMinerEffortBetweenCreatedAsync(con, pool.Id, address, startTime, clock.Now, ct));
+ if(minerEffort.HasValue)
+ stats.MinerEffort = minerEffort.Value;
+ }
stats.PerformanceSamples = await GetMinerPerformanceInternal(perfMode, pool, address, ct);
+
+ // add total confirmed and pending blocks
+ var totalConfirmedBlocks = await cf.Run(con => statsRepo.GetMinerTotalConfirmedBlocksAsync(con, pool.Id, address, ct));
+ var totalPendingBlocks = await cf.Run(con => statsRepo.GetMinerTotalPendingBlocksAsync(con, pool.Id, address, ct));
+ stats.TotalConfirmedBlocks = totalConfirmedBlocks;
+ stats.TotalPendingBlocks = totalPendingBlocks;
}
return stats;
@@ -511,7 +508,7 @@ public async Task PagePoolMinersAsync(
{
var pool = GetPool(poolId);
var ct = HttpContext.RequestAborted;
-
+
if(string.IsNullOrEmpty(address))
throw new ApiException("Invalid or missing miner address", HttpStatusCode.NotFound);
@@ -521,7 +518,7 @@ public async Task PagePoolMinersAsync(
var blockStates = state is { Length: > 0 } ?
state :
new[] { BlockStatus.Confirmed, BlockStatus.Pending, BlockStatus.Orphaned };
-
+
uint itemCount = await cf.Run(con => blocksRepo.GetMinerBlockCountAsync(con, poolId, address, ct));
uint pageCount = (uint) Math.Floor(itemCount / (double) pageSize);
@@ -601,7 +598,7 @@ public async Task PagePoolMinersAsync(
if(pool.Template.Family == CoinFamily.Ethereum)
address = address.ToLower();
-
+
uint itemCount = await cf.Run(con => paymentsRepo.GetPaymentsCountAsync(con, poolId, address, ct));
uint pageCount = (uint) Math.Floor(itemCount / (double) pageSize);
@@ -662,7 +659,7 @@ public async Task PagePoolMinersAsync(
if(pool.Template.Family == CoinFamily.Ethereum)
address = address.ToLower();
-
+
uint itemCount = await cf.Run(con => paymentsRepo.GetBalanceChangesCountAsync(con, poolId, address));
uint pageCount = (uint) Math.Floor(itemCount / (double) pageSize);
diff --git a/src/Miningcore/Api/Extensions/MiningPoolExtensions.cs b/src/Miningcore/Api/Extensions/MiningPoolExtensions.cs
index 301922bda0..c0ac2a86dd 100644
--- a/src/Miningcore/Api/Extensions/MiningPoolExtensions.cs
+++ b/src/Miningcore/Api/Extensions/MiningPoolExtensions.cs
@@ -6,6 +6,7 @@
using Miningcore.Blockchain.Ergo.Configuration;
using Miningcore.Blockchain.Handshake.Configuration;
using Miningcore.Blockchain.Kaspa.Configuration;
+using Miningcore.Blockchain.Warthog.Configuration;
using Miningcore.Configuration;
using Miningcore.Extensions;
using Miningcore.Mining;
@@ -51,6 +52,9 @@ public static PoolInfo ToPoolInfo(this PoolConfig poolConfig, IMapper mapper, Pe
case "kaspa":
extra.StripValue(nameof(KaspaPaymentProcessingConfigExtra.WalletPassword));
break;
+ case "warthog":
+ extra.StripValue(nameof(WarthogPaymentProcessingConfigExtra.WalletPrivateKey));
+ break;
}
}
diff --git a/src/Miningcore/Api/Requests/GetCoinMarketCapRequest.cs b/src/Miningcore/Api/Requests/GetCoinMarketCapRequest.cs
new file mode 100644
index 0000000000..825b6dc1fc
--- /dev/null
+++ b/src/Miningcore/Api/Requests/GetCoinMarketCapRequest.cs
@@ -0,0 +1,438 @@
+using Newtonsoft.Json;
+using System.Text.Json.Serialization;
+
+namespace Miningcore.Api.Requests
+{
+ public class CryptoApiResponse
+ {
+ [JsonPropertyName("status")]
+ [JsonProperty("status")]
+ public StatusInfo Status { get; set; }
+
+ [JsonPropertyName("data")]
+ [JsonProperty("data")]
+ public Dictionary> Data { get; set; }
+ }
+
+ public class StatusInfo
+ {
+ [JsonPropertyName("timestamp")]
+ [JsonProperty("timestamp")]
+ public DateTime? Timestamp { get; set; }
+
+ [JsonPropertyName("error_code")]
+ [JsonProperty("error_code")]
+ public int ErrorCode { get; set; }
+
+ [JsonPropertyName("error_message")]
+ [JsonProperty("error_message")]
+ public string ErrorMessage { get; set; }
+
+ [JsonPropertyName("elapsed")]
+ [JsonProperty("elapsed")]
+ public int Elapsed { get; set; }
+
+ [JsonPropertyName("credit_count")]
+ [JsonProperty("credit_count")]
+ public int CreditCount { get; set; }
+
+ [JsonPropertyName("notice")]
+ [JsonProperty("notice")]
+ public string Notice { get; set; }
+ }
+
+ public class CoinMarketCapData
+ {
+ [JsonPropertyName("id")]
+ [JsonProperty("id")]
+ public int Id { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("symbol")]
+ [JsonProperty("symbol")]
+ public string Symbol { get; set; }
+
+ [JsonPropertyName("logo")]
+ [JsonProperty("logo")]
+ public string Logo { get; set; } = "n/a";
+
+ [JsonPropertyName("slug")]
+ [JsonProperty("slug")]
+ public string Slug { get; set; }
+
+ [JsonPropertyName("num_market_pairs")]
+ [JsonProperty("num_market_pairs")]
+ public int? NumMarketPairs { get; set; }
+
+ [JsonPropertyName("date_added")]
+ [JsonProperty("date_added")]
+ public DateTime? DateAdded { get; set; }
+
+ [JsonPropertyName("tags")]
+ [JsonProperty("tags")]
+ public List Tags { get; set; }
+
+ [JsonPropertyName("max_supply")]
+ [JsonProperty("max_supply")]
+ public long? MaxSupply { get; set; }
+
+ [JsonPropertyName("circulating_supply")]
+ [JsonProperty("circulating_supply")]
+ public double CirculatingSupply { get; set; }
+
+ [JsonPropertyName("total_supply")]
+ [JsonProperty("total_supply")]
+ public double TotalSupply { get; set; }
+
+ [JsonPropertyName("is_active")]
+ [JsonProperty("is_active")]
+ public int IsActive { get; set; }
+
+ [JsonPropertyName("infinite_supply")]
+ [JsonProperty("infinite_supply")]
+ public bool InfiniteSupply { get; set; }
+
+ [JsonPropertyName("platform")]
+ [JsonProperty("platform")]
+ public Platform Platform { get; set; }
+
+ [JsonPropertyName("cmc_rank")]
+ [JsonProperty("cmc_rank")]
+ public int? CmcRank { get; set; }
+
+ [JsonPropertyName("is_fiat")]
+ [JsonProperty("is_fiat")]
+ public int IsFiat { get; set; }
+
+ [JsonPropertyName("self_reported_circulating_supply")]
+ [JsonProperty("self_reported_circulating_supply")]
+ public double? SelfReportedCirculatingSupply { get; set; }
+
+ [JsonPropertyName("self_reported_market_cap")]
+ [JsonProperty("self_reported_market_cap")]
+ public double? SelfReportedMarketCap { get; set; }
+
+ [JsonPropertyName("tvl_ratio")]
+ [JsonProperty("tvl_ratio")]
+ public double? TvlRatio { get; set; }
+
+ [JsonPropertyName("last_updated")]
+ [JsonProperty("last_updated")]
+ public DateTime? LastUpdated { get; set; }
+
+ [JsonPropertyName("quote")]
+ [JsonProperty("quote")]
+ public Dictionary Quote { get; set; }
+ }
+
+ public class Tag
+ {
+ [JsonPropertyName("slug")]
+ [JsonProperty("slug")]
+ public string Slug { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("category")]
+ [JsonProperty("category")]
+ public string Category { get; set; }
+ }
+
+ public class Platform
+ {
+ [JsonPropertyName("id")]
+ [JsonProperty("id")]
+ public int Id { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("symbol")]
+ [JsonProperty("symbol")]
+ public string Symbol { get; set; }
+
+ [JsonPropertyName("slug")]
+ [JsonProperty("slug")]
+ public string Slug { get; set; }
+
+ [JsonPropertyName("token_address")]
+ [JsonProperty("token_address")]
+ public string TokenAddress { get; set; }
+ }
+
+ public class Quote
+ {
+ [JsonPropertyName("price")]
+ [JsonProperty("price")]
+ public double? Price { get; set; }
+
+ [JsonPropertyName("volume_24h")]
+ [JsonProperty("volume_24h")]
+ public double? Volume24H { get; set; }
+
+ [JsonPropertyName("volume_change_24h")]
+ [JsonProperty("volume_change_24h")]
+ public double? VolumeChange24H { get; set; }
+
+ [JsonPropertyName("percent_change_1h")]
+ [JsonProperty("percent_change_1h")]
+ public double? PercentChange1H { get; set; }
+
+ [JsonPropertyName("percent_change_24h")]
+ [JsonProperty("percent_change_24h")]
+ public double? PercentChange24H { get; set; }
+
+ [JsonPropertyName("percent_change_7d")]
+ [JsonProperty("percent_change_7d")]
+ public double? PercentChange7D { get; set; }
+
+ [JsonPropertyName("percent_change_30d")]
+ [JsonProperty("percent_change_30d")]
+ public double? PercentChange30D { get; set; }
+
+ [JsonPropertyName("percent_change_60d")]
+ [JsonProperty("percent_change_60d")]
+ public double? PercentChange60D { get; set; }
+
+ [JsonPropertyName("percent_change_90d")]
+ [JsonProperty("percent_change_90d")]
+ public double? PercentChange90D { get; set; }
+
+ [JsonPropertyName("market_cap")]
+ [JsonProperty("market_cap")]
+ public double? MarketCap { get; set; }
+
+ [JsonPropertyName("market_cap_dominance")]
+ [JsonProperty("market_cap_dominance")]
+ public double? MarketCapDominance { get; set; }
+
+ [JsonPropertyName("fully_diluted_market_cap")]
+ [JsonProperty("fully_diluted_market_cap")]
+ public double? FullyDilutedMarketCap { get; set; }
+
+ [JsonPropertyName("tvl")]
+ [JsonProperty("tvl")]
+ public double? Tvl { get; set; }
+
+ [JsonPropertyName("last_updated")]
+ [JsonProperty("last_updated")]
+ public DateTime? LastUpdated { get; set; }
+ }
+
+
+ public class InfoApiResponse
+ {
+ [JsonPropertyName("status")]
+ [JsonProperty("status")]
+ public InfoStatus Status { get; set; }
+
+ [JsonPropertyName("data")]
+ [JsonProperty("data")]
+ public Dictionary Data { get; set; }
+ }
+
+ public class InfoStatus
+ {
+ [JsonPropertyName("timestamp")]
+ [JsonProperty("timestamp")]
+ public DateTime? Timestamp { get; set; }
+
+ [JsonPropertyName("error_code")]
+ [JsonProperty("error_code")]
+ public int ErrorCode { get; set; }
+
+ [JsonPropertyName("error_message")]
+ [JsonProperty("error_message")]
+ public string ErrorMessage { get; set; }
+
+ [JsonPropertyName("elapsed")]
+ [JsonProperty("elapsed")]
+ public int Elapsed { get; set; }
+
+ [JsonPropertyName("credit_count")]
+ [JsonProperty("credit_count")]
+ public int CreditCount { get; set; }
+
+ [JsonPropertyName("notice")]
+ [JsonProperty("notice")]
+ public string Notice { get; set; }
+ }
+
+ public class InfoCryptoData
+ {
+ [JsonPropertyName("id")]
+ [JsonProperty("id")]
+ public int Id { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("symbol")]
+ [JsonProperty("symbol")]
+ public string Symbol { get; set; }
+
+ [JsonPropertyName("category")]
+ [JsonProperty("category")]
+ public string Category { get; set; }
+
+ [JsonPropertyName("description")]
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonPropertyName("slug")]
+ [JsonProperty("slug")]
+ public string Slug { get; set; }
+
+ [JsonPropertyName("logo")]
+ [JsonProperty("logo")]
+ public string Logo { get; set; }
+
+ [JsonPropertyName("subreddit")]
+ [JsonProperty("subreddit")]
+ public string Subreddit { get; set; }
+
+ [JsonPropertyName("notice")]
+ [JsonProperty("notice")]
+ public string Notice { get; set; }
+
+ [JsonPropertyName("tags")]
+ [JsonProperty("tags")]
+ public List Tags { get; set; }
+
+ [JsonPropertyName("tag-names")]
+ [JsonProperty("tag-names")]
+ public List TagNames { get; set; }
+
+ [JsonPropertyName("tag-groups")]
+ [JsonProperty("tag-groups")]
+ public List TagGroups { get; set; }
+
+ [JsonPropertyName("urls")]
+ [JsonProperty("urls")]
+ public InfoUrls Urls { get; set; }
+
+ [JsonPropertyName("platform")]
+ [JsonProperty("platform")]
+ public InfoPlatform Platform { get; set; }
+
+ [JsonPropertyName("date_added")]
+ [JsonProperty("date_added")]
+ public DateTime? DateAdded { get; set; }
+
+ [JsonPropertyName("twitter_username")]
+ [JsonProperty("twitter_username")]
+ public string TwitterUsername { get; set; }
+
+ [JsonPropertyName("is_hidden")]
+ [JsonProperty("is_hidden")]
+ public int IsHidden { get; set; }
+
+ [JsonPropertyName("date_launched")]
+ [JsonProperty("date_launched")]
+ public DateTime? DateLaunched { get; set; }
+
+ [JsonPropertyName("contract_address")]
+ [JsonProperty("contract_address")]
+ public List ContractAddress { get; set; }
+
+ [JsonPropertyName("self_reported_circulating_supply")]
+ [JsonProperty("self_reported_circulating_supply")]
+ public decimal? SelfReportedCirculatingSupply { get; set; }
+
+ [JsonPropertyName("self_reported_tags")]
+ [JsonProperty("self_reported_tags")]
+ public List SelfReportedTags { get; set; }
+
+ [JsonPropertyName("self_reported_market_cap")]
+ [JsonProperty("self_reported_market_cap")]
+ public decimal? SelfReportedMarketCap { get; set; }
+
+ [JsonPropertyName("infinite_supply")]
+ [JsonProperty("infinite_supply")]
+ public bool InfiniteSupply { get; set; }
+ }
+
+ public class InfoUrls
+ {
+ [JsonPropertyName("website")]
+ [JsonProperty("website")]
+ public List Website { get; set; }
+
+ [JsonPropertyName("twitter")]
+ [JsonProperty("twitter")]
+ public List Twitter { get; set; }
+
+ [JsonPropertyName("message_board")]
+ [JsonProperty("message_board")]
+ public List MessageBoard { get; set; }
+
+ [JsonPropertyName("chat")]
+ [JsonProperty("chat")]
+ public List Chat { get; set; }
+
+ [JsonPropertyName("facebook")]
+ [JsonProperty("facebook")]
+ public List Facebook { get; set; }
+
+ [JsonPropertyName("explorer")]
+ [JsonProperty("explorer")]
+ public List Explorer { get; set; }
+
+ [JsonPropertyName("reddit")]
+ [JsonProperty("reddit")]
+ public List Reddit { get; set; }
+
+ [JsonPropertyName("technical_doc")]
+ [JsonProperty("technical_doc")]
+ public List TechnicalDoc { get; set; }
+
+ [JsonPropertyName("source_code")]
+ [JsonProperty("source_code")]
+ public List SourceCode { get; set; }
+
+ [JsonPropertyName("announcement")]
+ [JsonProperty("announcement")]
+ public List Announcement { get; set; }
+ }
+
+ public class InfoPlatform
+ {
+ [JsonPropertyName("id")]
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonPropertyName("name")]
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("slug")]
+ [JsonProperty("slug")]
+ public string Slug { get; set; }
+
+ [JsonPropertyName("symbol")]
+ [JsonProperty("symbol")]
+ public string Symbol { get; set; }
+
+ [JsonPropertyName("token_address")]
+ [JsonProperty("token_address")]
+ public string TokenAddress { get; set; }
+ }
+
+ public class InfoContractAddress
+ {
+ [JsonPropertyName("contract_address")]
+ [JsonProperty("contract_address")]
+ public string Address { get; set; }
+
+ [JsonPropertyName("platform")]
+ [JsonProperty("platform")]
+ public InfoPlatform Platform { get; set; }
+ }
+}
diff --git a/src/Miningcore/Api/Responses/GetCoinMarketCapResponse.cs b/src/Miningcore/Api/Responses/GetCoinMarketCapResponse.cs
new file mode 100644
index 0000000000..e27d1b39c4
--- /dev/null
+++ b/src/Miningcore/Api/Responses/GetCoinMarketCapResponse.cs
@@ -0,0 +1,7 @@
+namespace Miningcore.Api.Responses
+{
+ public class CoinMarketCapResponse
+ {
+ public double USDC { get; set; }
+ }
+}
diff --git a/src/Miningcore/Api/Responses/GetMinerStatsResponse.cs b/src/Miningcore/Api/Responses/GetMinerStatsResponse.cs
index 7b16043abf..8c1672665a 100644
--- a/src/Miningcore/Api/Responses/GetMinerStatsResponse.cs
+++ b/src/Miningcore/Api/Responses/GetMinerStatsResponse.cs
@@ -30,4 +30,6 @@ public class MinerStats
public string LastPaymentLink { get; set; }
public WorkerPerformanceStatsContainer Performance { get; set; }
public WorkerPerformanceStatsContainer[] PerformanceSamples { get; set; }
+ public long TotalConfirmedBlocks { get; set; }
+ public long TotalPendingBlocks { get; set; }
}
diff --git a/src/Miningcore/Api/Responses/GetPoolStatsResponse.cs b/src/Miningcore/Api/Responses/GetPoolStatsResponse.cs
index 394a312ca5..65c5f0530f 100644
--- a/src/Miningcore/Api/Responses/GetPoolStatsResponse.cs
+++ b/src/Miningcore/Api/Responses/GetPoolStatsResponse.cs
@@ -2,9 +2,9 @@ namespace Miningcore.Api.Responses;
public partial class AggregatedPoolStats
{
- public double PoolHashrate { get; set; }
+ public float PoolHashrate { get; set; }
public int ConnectedMiners { get; set; }
- public double ValidSharesPerSecond { get; set; }
+ public int ValidSharesPerSecond { get; set; }
public double NetworkHashrate { get; set; }
public double NetworkDifficulty { get; set; }
diff --git a/src/Miningcore/Api/Responses/GetPoolsResponse.cs b/src/Miningcore/Api/Responses/GetPoolsResponse.cs
index 624c5009ac..beac7d1046 100644
--- a/src/Miningcore/Api/Responses/GetPoolsResponse.cs
+++ b/src/Miningcore/Api/Responses/GetPoolsResponse.cs
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
+using Miningcore.Api.Requests;
using Miningcore.Blockchain;
using Miningcore.Configuration;
using Miningcore.Mining;
@@ -30,23 +31,22 @@ public class ApiCoinConfig
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Discord { get; set; }
- [System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public string Github{ get; set; }
-
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Telegram { get; set; }
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string CanonicalName { get; set; }
-}
-public class ApiPoolPayoutSchemeConfig
-{
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public decimal? Factor { get; set; } = 2.0m;
+ public object Price { get; set; }
+
+ [System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string Logo { get; set; }
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- public decimal? BlockFinderPercentage { get; set; } = 5.0m;
+ [JsonPropertyName("volume_change_24h")]
+ [Newtonsoft.Json.JsonProperty("volume_change_24h")]
+ public object VolumeChange24H { get; set; }
}
public class ApiPoolPaymentProcessingConfig
@@ -54,7 +54,7 @@ public class ApiPoolPaymentProcessingConfig
public bool Enabled { get; set; }
public decimal MinimumPayment { get; set; } // in pool-base-currency (ie. Bitcoin, not Satoshis)
public string PayoutScheme { get; set; }
- public ApiPoolPayoutSchemeConfig PayoutSchemeConfig { get; set; }
+ public JToken PayoutSchemeConfig { get; set; }
[Newtonsoft.Json.JsonExtensionData]
public IDictionary Extra { get; set; }
@@ -83,6 +83,9 @@ public partial class PoolInfo
public MinerPerformanceStats[] TopMiners { get; set; }
public decimal TotalPaid { get; set; }
public uint TotalBlocks { get; set; }
+ public uint TotalConfirmedBlocks { get; set; }
+ public uint TotalPendingBlocks { get; set; }
+ public decimal BlockReward { get; set; }
public DateTime? LastPoolBlockTime { get; set; }
public double PoolEffort { get; set; }
}
diff --git a/src/Miningcore/AutoMapperProfile.cs b/src/Miningcore/AutoMapperProfile.cs
index 42c4d07e59..38f88b2b2e 100644
--- a/src/Miningcore/AutoMapperProfile.cs
+++ b/src/Miningcore/AutoMapperProfile.cs
@@ -47,7 +47,6 @@ public AutoMapperProfile()
.ForMember(dest => dest.Market, opt => opt.MapFrom(src => src.Market))
.ForMember(dest => dest.Twitter, opt => opt.MapFrom(src => src.Twitter))
.ForMember(dest => dest.Discord, opt => opt.MapFrom(src => src.Discord))
- .ForMember(dest => dest.Github, opt => opt.MapFrom(src => src.Github))
.ForMember(dest => dest.Telegram, opt => opt.MapFrom(src => src.Telegram))
.ForMember(dest => dest.Algorithm, opt => opt.MapFrom(src => src.GetAlgorithmName()));
@@ -64,7 +63,9 @@ public AutoMapperProfile()
CreateMap()
.ForMember(dest => dest.LastPayment, opt => opt.Ignore())
- .ForMember(dest => dest.LastPaymentLink, opt => opt.Ignore());
+ .ForMember(dest => dest.LastPaymentLink, opt => opt.Ignore())
+ .ForMember(dest => dest.TotalConfirmedBlocks, opt => opt.MapFrom(src => src.TotalConfirmedBlocks))
+ .ForMember(dest => dest.TotalPendingBlocks, opt => opt.MapFrom(src => src.TotalPendingBlocks));
CreateMap();
CreateMap();
diff --git a/src/Miningcore/AutofacModule.cs b/src/Miningcore/AutofacModule.cs
index d03faaf0c6..fb939c9497 100644
--- a/src/Miningcore/AutofacModule.cs
+++ b/src/Miningcore/AutofacModule.cs
@@ -14,6 +14,9 @@
using Miningcore.Blockchain.Kaspa;
using Miningcore.Blockchain.Nexa;
using Miningcore.Blockchain.Progpow;
+using Miningcore.Blockchain.Warthog;
+using Miningcore.Blockchain.Xelis;
+using Miningcore.Blockchain.Zano;
using Miningcore.Configuration;
using Miningcore.Crypto;
using Miningcore.Crypto.Hashing.Equihash;
@@ -32,6 +35,7 @@
using Microsoft.IO;
using Miningcore.Nicehash;
using Miningcore.Pushover;
+using Miningcore.CoinMarketCap;
namespace Miningcore;
@@ -122,6 +126,9 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType()
.SingleInstance();
+ builder.RegisterType()
+ .SingleInstance();
+
builder.RegisterType()
.SingleInstance();
@@ -213,6 +220,7 @@ protected override void Load(ContainerBuilder builder)
//////////////////////
// Handshake
+
builder.RegisterType();
//////////////////////
@@ -222,6 +230,7 @@ protected override void Load(ContainerBuilder builder)
//////////////////////
// Nexa
+
builder.RegisterType();
//////////////////////
@@ -229,6 +238,21 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType();
+ //////////////////////
+ // Warthog
+
+ builder.RegisterType();
+
+ //////////////////////
+ // Xelis
+
+ builder.RegisterType();
+
+ //////////////////////
+ // Zano
+
+ builder.RegisterType();
+
base.Load(builder);
}
}
diff --git a/src/Miningcore/Blockchain/Alephium/AlephiumConstants.cs b/src/Miningcore/Blockchain/Alephium/AlephiumConstants.cs
index ce778a4289..51a2068805 100644
--- a/src/Miningcore/Blockchain/Alephium/AlephiumConstants.cs
+++ b/src/Miningcore/Blockchain/Alephium/AlephiumConstants.cs
@@ -28,6 +28,7 @@ public static class AlephiumConstants
// Socket miner API
public const int MessageHeaderSize = 4; // 4 bytes body length
+ public const byte MiningProtocolVersion = 0x01;
public const byte JobsMessageType = 0x00;
public const byte SubmitResultMessageType = 0x01;
public const byte SubmitBlockMessageType = 0x00;
diff --git a/src/Miningcore/Blockchain/Alephium/AlephiumJob.cs b/src/Miningcore/Blockchain/Alephium/AlephiumJob.cs
index f12a186560..810d05b28d 100644
--- a/src/Miningcore/Blockchain/Alephium/AlephiumJob.cs
+++ b/src/Miningcore/Blockchain/Alephium/AlephiumJob.cs
@@ -33,7 +33,7 @@ public class AlephiumJob
private AlephiumJobParams jobParams;
private readonly ConcurrentDictionary submissions = new(StringComparer.OrdinalIgnoreCase);
- private static readonly IHashAlgorithm hasher = new Blake3IHash();
+ private static readonly IHashAlgorithm hasher = new Blake3();
private static byte[] GetBigEndianUInt32(uint value)
{
@@ -56,18 +56,23 @@ protected bool RegisterSubmit(string nonce)
return submissions.TryAdd(key, true);
}
- public virtual byte[] SerializeCoinbase(string nonce)
+ public virtual byte[] SerializeCoinbase(string nonce, int socketMiningProtocol = 0)
{
var nonceBytes = (Span) nonce.HexToByteArray();
var headerBlobBytes = (Span) BlockTemplate.HeaderBlob.HexToByteArray();
var txsBlobBytes = (Span) BlockTemplate.TxsBlob.HexToByteArray();
-
+
uint blockSize = (uint)nonceBytes.Length + (uint)headerBlobBytes.Length + (uint)txsBlobBytes.Length;
- uint messageSize = 4 + 1 + blockSize; // encodedBlockSize(4 bytes) + messageType(1 byte)
-
+ int messagePrefixSize = (socketMiningProtocol > 0) ? 1 + 1 + 4: 4 + 1; // socketMiningProtocol: 0 => encodedBlockSize(4 bytes) + messageType(1 byte) || socketMiningProtocol: 1 => version(1 byte) + messageType(1 byte) + encodedBlockSize(4 bytes)
+ uint messageSize = (uint)messagePrefixSize + blockSize;
+
using(var stream = new MemoryStream())
{
stream.Write(GetBigEndianUInt32(messageSize));
+
+ if(socketMiningProtocol > 0)
+ stream.WriteByte(AlephiumConstants.MiningProtocolVersion);
+
stream.WriteByte(AlephiumConstants.SubmitBlockMessageType);
stream.Write(GetBigEndianUInt32(blockSize));
stream.Write(nonceBytes);
diff --git a/src/Miningcore/Blockchain/Alephium/AlephiumJobManager.cs b/src/Miningcore/Blockchain/Alephium/AlephiumJobManager.cs
index 5e8975aaa7..1df0e2af84 100644
--- a/src/Miningcore/Blockchain/Alephium/AlephiumJobManager.cs
+++ b/src/Miningcore/Blockchain/Alephium/AlephiumJobManager.cs
@@ -47,13 +47,13 @@ public AlephiumJobManager(
private AlephiumCoinTemplate coin;
private AlephiumClient rpc;
private string network;
- private readonly List validJobs = new();
private readonly IExtraNonceProvider extraNonceProvider;
private readonly IMasterClock clock;
private AlephiumPoolConfigExtra extraPoolConfig;
private AlephiumPaymentProcessingConfigExtra extraPoolPaymentProcessingConfig;
protected int maxActiveJobs;
private int socketJobMessageBufferSize;
+ private int socketMiningProtocol = 0;
protected IObservable AlephiumSubscribeStratumApiSocketClient(CancellationToken ct, DaemonEndpointConfig endPoint,
AlephiumDaemonEndpointConfigExtra extraDaemonEndpoint, object payload = null,
@@ -92,6 +92,7 @@ protected IObservable AlephiumSubscribeStratumApiSocket
int receivedBytes;
// Message
+ byte messageVersion = AlephiumConstants.MiningProtocolVersion; // only available since node release >= "3.6.0"
byte messageType;
uint messageLength;
@@ -106,6 +107,7 @@ protected IObservable AlephiumSubscribeStratumApiSocket
uint jobSize;
int fromGroup;
int toGroup;
+ ulong height = 0;
uint headerBlobLength;
byte[] headerBlob;
uint txsBlobLength;
@@ -141,8 +143,14 @@ protected IObservable AlephiumSubscribeStratumApiSocket
if (bufferArray.Length >= messageLength + AlephiumConstants.MessageHeaderSize)
{
+ if(socketMiningProtocol > 0)
+ {
+ messageVersion = reader.ReadByte();
+ logger.Debug(() => $"Message version: {messageVersion}");
+ }
+
messageType = reader.ReadByte();
- if (messageType == AlephiumConstants.JobsMessageType)
+ if (messageVersion == AlephiumConstants.MiningProtocolVersion && messageType == AlephiumConstants.JobsMessageType)
{
jobSize = ReadBigEndianUInt32(reader);
logger.Debug(() => $"Parsing {jobSize} job(s) :D");
@@ -156,8 +164,12 @@ protected IObservable AlephiumSubscribeStratumApiSocket
toGroup = (int)ReadBigEndianUInt32(reader);
logger.Debug(() => $"fromGroup: {fromGroup} - toGroup: {toGroup}");
- chainInfo = await rpc.GetBlockflowChainInfoAsync(fromGroup, toGroup, cts.Token);
- logger.Debug(() => $"Height: {chainInfo?.CurrentHeight + 1}");
+ if(socketMiningProtocol == 0)
+ {
+ chainInfo = await rpc.GetBlockflowChainInfoAsync(fromGroup, toGroup, cts.Token);
+ height = (ulong) chainInfo?.CurrentHeight + 1;
+ logger.Debug(() => $"height: {height}");
+ }
headerBlobLength = ReadBigEndianUInt32(reader);
logger.Debug(() => $"headerBlobLength: {headerBlobLength}");
@@ -174,10 +186,16 @@ protected IObservable AlephiumSubscribeStratumApiSocket
targetBlob = reader.ReadBytes((int)targetLength);
logger.Debug(() => $"targetBlob: {targetBlob.ToHexString()}");
+ if(socketMiningProtocol > 0)
+ {
+ height = (ulong)ReadBigEndianUInt32(reader);
+ logger.Debug(() => $"height: {height}");
+ }
+
alephiumBlockTemplate[index] = new AlephiumBlockTemplate
{
JobId = NextJobId("X"),
- Height = (ulong) chainInfo?.CurrentHeight + 1,
+ Height = height,
Timestamp = clock.Now,
FromGroup = fromGroup,
ToGroup = toGroup,
@@ -196,7 +214,7 @@ protected IObservable AlephiumSubscribeStratumApiSocket
}
else
{
- logger.Debug(() => $"Unknown message type, wait for more data...");
+ logger.Warn(() => $"Message version: {messageVersion}, expects {AlephiumConstants.MiningProtocolVersion} - Message type: {messageType}, expects {AlephiumConstants.JobsMessageType}, wait for more data...");
break;
}
}
@@ -323,15 +341,6 @@ private async Task UpdateJob(CancellationToken ct, string via = null, Alep
job.Init(blockTemplate);
- lock(jobLock)
- {
- validJobs.Insert(0, job);
-
- // trim active jobs
- while(validJobs.Count > maxActiveJobs)
- validJobs.RemoveAt(validJobs.Count - 1);
- }
-
if(via != null)
logger.Info(() => $"Detected new block {job.BlockTemplate.Height} on chain[{job.BlockTemplate.ChainIndex}] [{via}]");
else
@@ -431,8 +440,10 @@ private async Task SubmitBlockAsync(CancellationToken ct, DaemonEndpointCo
int receivedBytes;
// Message
+ int messageTypeOffset;
byte messageType;
int startOffset;
+ int succeedIndex;
byte[] message;
logger.Debug(() => $"[Submit Block] - Submitting coinbase");
@@ -444,15 +455,17 @@ private async Task SubmitBlockAsync(CancellationToken ct, DaemonEndpointCo
{
logger.Debug(() => $"{receivedBytes} byte(s) of data have been received");
- messageType = receiveBuffer[AlephiumConstants.MessageHeaderSize];
+ messageTypeOffset = (socketMiningProtocol > 0) ? 1: 0; // socketMiningProtocol: 0 => messageType(1 byte) || socketMiningProtocol: 1 => version(1 byte) + messageType(1 byte)
+ messageType = receiveBuffer[AlephiumConstants.MessageHeaderSize + messageTypeOffset];
if (messageType == AlephiumConstants.SubmitResultMessageType)
{
logger.Debug(() => $"[Submit Block] - Response received :D");
- startOffset = AlephiumConstants.MessageHeaderSize + 1; // 1 byte message type
+ startOffset = AlephiumConstants.MessageHeaderSize + messageTypeOffset + 1; // 1 byte
message = new byte[receivedBytes - startOffset];
Buffer.BlockCopy(receiveBuffer, startOffset, message, 0, receivedBytes - startOffset);
- succeed = (message[8] == 1);
+ succeedIndex = (socketMiningProtocol > 0) ? 40: 8; // socketMiningProtocol: 0 => succeed: index[8] || socketMiningProtocol: 1 => succeed: index[40]
+ succeed = (message[succeedIndex] == 1);
break;
}
}
@@ -507,9 +520,20 @@ public async ValueTask SubmitShareAsync(StratumConnection worker, Alephiu
AlephiumJob job;
- lock(jobLock)
+ lock(context)
{
- job = validJobs.FirstOrDefault(x => x.JobId == jobId);
+ job = context.GetJob(jobId);
+
+ if(job == null)
+ {
+ // stupid hack for busted ass IceRiver ASICs. Need to loop
+ // through job using blockTemplate "FromGroup" & "ToGroup" because they submit jobs with incorrect IDs
+ if(ValidateIsIceRiverMiner(context.UserAgent))
+ job = context.validJobs.ToArray().FirstOrDefault(x => x.BlockTemplate.FromGroup == submitParams.FromGroup && x.BlockTemplate.ToGroup == submitParams.ToGroup);
+ }
+
+ if(job == null)
+ logger.Warn(() => $"[{context.Miner}] => jobId: {jobId} - Last known job: {context.validJobs.ToArray().FirstOrDefault()?.JobId}");
}
if(job == null)
@@ -535,7 +559,7 @@ public async ValueTask SubmitShareAsync(StratumConnection worker, Alephiu
// Prepare data for the stratum API socket
var daemonEndpoint = daemonEndpoints.First();
var extraDaemonEndpoint = daemonEndpoint.Extra.SafeExtensionDataAs();
- var jobCoinbase = job.SerializeCoinbase(nonce);
+ var jobCoinbase = job.SerializeCoinbase(nonce, socketMiningProtocol);
var acceptResponse = await SubmitBlockAsync(ct, daemonEndpoint,
extraDaemonEndpoint, jobCoinbase);
@@ -724,7 +748,17 @@ protected override async Task AreDaemonsConnectedAsync(CancellationToken c
// update stats
if(!string.IsNullOrEmpty(nodeInfo?.BuildInfo.ReleaseVersion))
- BlockchainStats.NodeVersion = nodeInfo?.BuildInfo.ReleaseVersion;
+ {
+ BlockchainStats.NodeVersion = nodeInfo.BuildInfo.ReleaseVersion;
+
+ // update socket mining protocol
+ string numbersOnly = Regex.Replace(nodeInfo.BuildInfo.ReleaseVersion, "[^0-9.]", "");
+ string[] numbers = numbersOnly.Split(".");
+
+ // update socket mining protocol
+ if(numbers.Length >= 2)
+ socketMiningProtocol = ((Convert.ToUInt32(numbers[0]) > 3) || (Convert.ToUInt32(numbers[0]) == 3 && Convert.ToUInt32(numbers[1]) >= 6)) ? 1: 0;
+ }
if(infosChainParams?.NetworkId != 7)
return info?.Count > 0;
@@ -767,5 +801,11 @@ private AlephiumJobParams GetJobParamsForStratum()
return job?.GetJobParams();
}
+ public override AlephiumJob GetJobForStratum()
+ {
+ var job = currentJob;
+ return job;
+ }
+
#endregion // Overrides
}
\ No newline at end of file
diff --git a/src/Miningcore/Blockchain/Alephium/AlephiumPool.cs b/src/Miningcore/Blockchain/Alephium/AlephiumPool.cs
index 4a601f127e..70efb855f1 100644
--- a/src/Miningcore/Blockchain/Alephium/AlephiumPool.cs
+++ b/src/Miningcore/Blockchain/Alephium/AlephiumPool.cs
@@ -40,7 +40,6 @@ public AlephiumPool(IComponentContext ctx,
{
}
- protected AlephiumJobParams currentJobParams;
protected AlephiumJobManager manager;
private AlephiumPoolConfigExtra extraPoolConfig;
private AlephiumCoinTemplate coin;
@@ -71,7 +70,7 @@ protected virtual async Task OnHelloAsync(StratumConnection connection, Timestam
// [Respect the goddamn standards Nicehack :(]
var response = new JsonRpcResponse