From c10ec90b60b5d3894d2e3c70629414e1f200bf62 Mon Sep 17 00:00:00 2001
From: SChernykh <sergey.v.chernykh@gmail.com>
Date: Sun, 15 Nov 2020 20:38:27 +0100
Subject: [PATCH 1/6] Make single thread bench cheat-resistant

Each hash is dependent on the previous hash to make multi-threaded cheating impossible.
---
 src/backend/common/interfaces/IWorker.h |  3 ++-
 src/backend/cpu/CpuBackend.cpp          |  2 +-
 src/backend/cpu/CpuWorker.cpp           | 34 +++++++++++++++++++++----
 src/backend/cpu/CpuWorker.h             |  3 ++-
 src/backend/cuda/CudaBackend.cpp        |  2 +-
 src/backend/cuda/CudaWorker.cpp         |  2 +-
 src/backend/cuda/CudaWorker.h           |  3 ++-
 src/backend/opencl/OclBackend.cpp       |  2 +-
 src/backend/opencl/OclWorker.cpp        |  2 +-
 src/backend/opencl/OclWorker.h          |  3 ++-
 src/crypto/randomx/jit_compiler_x86.cpp |  2 +-
 11 files changed, 43 insertions(+), 15 deletions(-)

diff --git a/src/backend/common/interfaces/IWorker.h b/src/backend/common/interfaces/IWorker.h
index dcc925dcd..979d998ff 100644
--- a/src/backend/common/interfaces/IWorker.h
+++ b/src/backend/common/interfaces/IWorker.h
@@ -35,6 +35,7 @@ namespace xmrig {
 
 class VirtualMemory;
 class Job;
+class Config;
 
 
 class IWorker
@@ -48,7 +49,7 @@ public:
     virtual size_t intensity() const                          = 0;
     virtual uint64_t rawHashes() const                        = 0;
     virtual void getHashrateData(uint64_t&, uint64_t&) const  = 0;
-    virtual void start()                                      = 0;
+    virtual void start(Config*)                               = 0;
     virtual void jobEarlyNotification(const Job&)             = 0;
 
 #   ifdef XMRIG_FEATURE_BENCHMARK
diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp
index ecce64fe1..a847567ae 100644
--- a/src/backend/cpu/CpuBackend.cpp
+++ b/src/backend/cpu/CpuBackend.cpp
@@ -390,7 +390,7 @@ void xmrig::CpuBackend::start(IWorker *worker, bool ready)
     mutex.unlock();
 
     if (ready) {
-        worker->start();
+        worker->start(d_ptr->controller->config());
     }
 }
 
diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp
index fd1a5932b..4c7781c3c 100644
--- a/src/backend/cpu/CpuWorker.cpp
+++ b/src/backend/cpu/CpuWorker.cpp
@@ -30,6 +30,7 @@
 
 #include "backend/cpu/CpuWorker.h"
 #include "base/tools/Chrono.h"
+#include "core/config/Config.h"
 #include "core/Miner.h"
 #include "crypto/cn/CnCtx.h"
 #include "crypto/cn/CryptoNight_test.h"
@@ -198,7 +199,7 @@ bool xmrig::CpuWorker<N>::selfTest()
 
 
 template<size_t N>
-void xmrig::CpuWorker<N>::start()
+void xmrig::CpuWorker<N>::start(xmrig::Config* config)
 {
     while (Nonce::sequence(Nonce::CPU) > 0) {
         if (Nonce::isPaused()) {
@@ -219,6 +220,10 @@ void xmrig::CpuWorker<N>::start()
         alignas(16) uint64_t tempHash[8] = {};
 #       endif
 
+#       ifdef XMRIG_FEATURE_BENCHMARK
+        const size_t benchThreads = config->cpu().threads().get(m_job.currentJob().algorithm()).count();
+#       endif
+
         while (!Nonce::isOutdated(Nonce::CPU, m_job.sequence())) {
             const Job &job = m_job.currentJob();
 
@@ -231,6 +236,29 @@ void xmrig::CpuWorker<N>::start()
                 current_job_nonces[i] = *m_job.nonce(i);
             }
 
+#           ifdef XMRIG_FEATURE_BENCHMARK
+            if (m_benchSize) {
+                bool done = true;
+                for (size_t i = 0; i < N; ++i) {
+                    if (current_job_nonces[i] < m_benchSize) {
+                        done = false;
+                        break;
+                    }
+                }
+
+                // Stop benchmark when all hashes have been counted
+                if (done) {
+                    m_benchDoneTime = Chrono::steadyMSecs();
+                    return;
+                }
+
+                // Make each hash dependent on the previous one in single thread benchmark to prevent cheating with multiple threads
+                if (benchThreads == 1) {
+                    *(uint64_t*)(m_job.blob()) ^= m_benchData;
+                }
+            }
+#           endif
+
             bool valid = true;
 
 #           ifdef XMRIG_ALGO_RANDOMX
@@ -274,10 +302,6 @@ void xmrig::CpuWorker<N>::start()
                         if (current_job_nonces[i] < m_benchSize) {
                             m_benchData ^= value;
                         }
-                        else {
-                            m_benchDoneTime = Chrono::steadyMSecs();
-                            return;
-                        }
                     }
                     else
 #                   endif
diff --git a/src/backend/cpu/CpuWorker.h b/src/backend/cpu/CpuWorker.h
index 087156c24..a43837812 100644
--- a/src/backend/cpu/CpuWorker.h
+++ b/src/backend/cpu/CpuWorker.h
@@ -43,6 +43,7 @@ namespace xmrig {
 
 
 class RxVm;
+class Config;
 
 
 template<size_t N>
@@ -56,7 +57,7 @@ public:
 
 protected:
     bool selfTest() override;
-    void start() override;
+    void start(Config*) override;
 
     inline const VirtualMemory *memory() const override { return m_memory; }
     inline size_t intensity() const override            { return N; }
diff --git a/src/backend/cuda/CudaBackend.cpp b/src/backend/cuda/CudaBackend.cpp
index ab5b22e53..f347aacfd 100644
--- a/src/backend/cuda/CudaBackend.cpp
+++ b/src/backend/cuda/CudaBackend.cpp
@@ -481,7 +481,7 @@ void xmrig::CudaBackend::start(IWorker *worker, bool ready)
     mutex.unlock();
 
     if (ready) {
-        worker->start();
+        worker->start(d_ptr->controller->config());
     }
 }
 
diff --git a/src/backend/cuda/CudaWorker.cpp b/src/backend/cuda/CudaWorker.cpp
index 7bb8844c1..9e34e8500 100644
--- a/src/backend/cuda/CudaWorker.cpp
+++ b/src/backend/cuda/CudaWorker.cpp
@@ -145,7 +145,7 @@ size_t xmrig::CudaWorker::intensity() const
 }
 
 
-void xmrig::CudaWorker::start()
+void xmrig::CudaWorker::start(xmrig::Config*)
 {
     while (Nonce::sequence(Nonce::CUDA) > 0) {
         if (!isReady()) {
diff --git a/src/backend/cuda/CudaWorker.h b/src/backend/cuda/CudaWorker.h
index e82e34254..9ccdc519b 100644
--- a/src/backend/cuda/CudaWorker.h
+++ b/src/backend/cuda/CudaWorker.h
@@ -39,6 +39,7 @@ namespace xmrig {
 
 
 class ICudaRunner;
+class Config;
 
 
 class CudaWorker : public Worker
@@ -58,7 +59,7 @@ public:
 protected:
     bool selfTest() override;
     size_t intensity() const override;
-    void start() override;
+    void start(Config*) override;
 
 private:
     bool consumeJob();
diff --git a/src/backend/opencl/OclBackend.cpp b/src/backend/opencl/OclBackend.cpp
index 7b99700b5..04bc2cdb5 100644
--- a/src/backend/opencl/OclBackend.cpp
+++ b/src/backend/opencl/OclBackend.cpp
@@ -463,7 +463,7 @@ void xmrig::OclBackend::start(IWorker *worker, bool ready)
     mutex.unlock();
 
     if (ready) {
-        worker->start();
+        worker->start(d_ptr->controller->config());
     }
 }
 
diff --git a/src/backend/opencl/OclWorker.cpp b/src/backend/opencl/OclWorker.cpp
index ac493ab55..ca6cfaccf 100644
--- a/src/backend/opencl/OclWorker.cpp
+++ b/src/backend/opencl/OclWorker.cpp
@@ -163,7 +163,7 @@ size_t xmrig::OclWorker::intensity() const
 }
 
 
-void xmrig::OclWorker::start()
+void xmrig::OclWorker::start(xmrig::Config*)
 {
     cl_uint results[0x100];
 
diff --git a/src/backend/opencl/OclWorker.h b/src/backend/opencl/OclWorker.h
index 403a0765d..d43ccf561 100644
--- a/src/backend/opencl/OclWorker.h
+++ b/src/backend/opencl/OclWorker.h
@@ -40,6 +40,7 @@ namespace xmrig {
 
 class IOclRunner;
 class Job;
+class Config;
 
 
 class OclWorker : public Worker
@@ -59,7 +60,7 @@ public:
 protected:
     bool selfTest() override;
     size_t intensity() const override;
-    void start() override;
+    void start(Config*) override;
 
 private:
     bool consumeJob();
diff --git a/src/crypto/randomx/jit_compiler_x86.cpp b/src/crypto/randomx/jit_compiler_x86.cpp
index 88c1ce4ef..182de1fa6 100644
--- a/src/crypto/randomx/jit_compiler_x86.cpp
+++ b/src/crypto/randomx/jit_compiler_x86.cpp
@@ -297,7 +297,7 @@ namespace randomx {
 	}
 
 	void JitCompilerX86::generateProgramPrologue(Program& prog, ProgramConfiguration& pcfg) {
-		codePos = ((uint8_t*)randomx_program_prologue_first_load) - ((uint8_t*)randomx_program_prologue);
+		codePos = ADDR(randomx_program_prologue_first_load) - ADDR(randomx_program_prologue);
 		code[codePos + 2] = 0xc0 + pcfg.readReg0;
 		code[codePos + 5] = 0xc0 + pcfg.readReg1;
 		*(uint32_t*)(code + codePos + 10) = RandomX_CurrentConfig.ScratchpadL3Mask64_Calculated;

From ee677ef5c987d1fbcb38dc6fec05eb2433494d23 Mon Sep 17 00:00:00 2001
From: SChernykh <sergey.v.chernykh@gmail.com>
Date: Mon, 16 Nov 2020 00:57:00 +0100
Subject: [PATCH 2/6] Added reference hashes for 1T offline benchmark

---
 src/backend/common/Benchmark.cpp | 35 +++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/src/backend/common/Benchmark.cpp b/src/backend/common/Benchmark.cpp
index ba1daf1cb..4e9b696b8 100644
--- a/src/backend/common/Benchmark.cpp
+++ b/src/backend/common/Benchmark.cpp
@@ -69,6 +69,38 @@ static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck = {
 };
 
 
+static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck1T = {
+    { Algorithm::RX_0, {
+        {   250000U, 0x90A15B799486F3EBULL },
+        {   500000U, 0xAA83118FEE570F9AULL },
+        {  1000000U, 0x3DF47B0A427C93D9ULL },
+        {  2000000U, 0xED4D639B0AEB85C6ULL },
+        {  3000000U, 0x2D4F9B4275A713C3ULL },
+        {  4000000U, 0xA9EBE4888377F8D3ULL },
+        {  5000000U, 0xB92F81851E180454ULL },
+        {  6000000U, 0xFB9F98F63C2F1B7DULL },
+        {  7000000U, 0x2CC3D7A779D5AB35ULL },
+        {  8000000U, 0x2EEF833EA462F4B1ULL },
+        {  9000000U, 0xC6D39EF59213A07CULL },
+        { 10000000U, 0x95E6BAE68DD779CDULL }
+    }},
+    { Algorithm::RX_WOW, {
+        {   250000U, 0x7B409F096C863207ULL },
+        {   500000U, 0x70B7B80D15654216ULL },
+        {  1000000U, 0x31301CC550306A59ULL },
+        {  2000000U, 0x92F65E9E31116361ULL },
+        {  3000000U, 0x7FE8DF6F43BA5285ULL },
+        {  4000000U, 0xD6CDA54FE4D9BBF7ULL },
+        {  5000000U, 0x73AF673E1A38E2B4ULL },
+        {  6000000U, 0x81FDC5C4B45D84E4ULL },
+        {  7000000U, 0xAA08CA57666DC874ULL },
+        {  8000000U, 0x9DCEFB833FC875BCULL },
+        {  9000000U, 0x862F051352CFCA1FULL },
+        { 10000000U, 0xC403F220189E8430ULL }
+    }}
+};
+
+
 } // namespace xmrig
 
 
@@ -214,7 +246,8 @@ uint64_t xmrig::Benchmark::referenceHash() const
     uint64_t hash = 0;
 
     try {
-        hash = hashCheck.at(m_algo).at(m_end);
+        const auto& h = (m_workers == 1) ? hashCheck1T : hashCheck;
+        hash = h.at(m_algo).at(m_end);
     } catch (const std::exception &ex) {}
 
     return hash;

From 926871cbe1386728ffad8b0aeddb1ac8108501c5 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Mon, 16 Nov 2020 07:58:28 +0700
Subject: [PATCH 3/6] Removed non thread safe access to config.

---
 src/backend/common/interfaces/IWorker.h | 7 +++----
 src/backend/cpu/CpuBackend.cpp          | 2 +-
 src/backend/cpu/CpuConfig.cpp           | 9 +++++----
 src/backend/cpu/CpuLaunchData.cpp       | 3 ++-
 src/backend/cpu/CpuLaunchData.h         | 3 ++-
 src/backend/cpu/CpuWorker.cpp           | 9 +++------
 src/backend/cpu/CpuWorker.h             | 4 ++--
 src/backend/cuda/CudaBackend.cpp        | 2 +-
 src/backend/cuda/CudaWorker.cpp         | 2 +-
 src/backend/cuda/CudaWorker.h           | 3 +--
 src/backend/opencl/OclBackend.cpp       | 2 +-
 src/backend/opencl/OclWorker.cpp        | 2 +-
 src/backend/opencl/OclWorker.h          | 3 +--
 13 files changed, 24 insertions(+), 27 deletions(-)

diff --git a/src/backend/common/interfaces/IWorker.h b/src/backend/common/interfaces/IWorker.h
index 979d998ff..b37afcaf8 100644
--- a/src/backend/common/interfaces/IWorker.h
+++ b/src/backend/common/interfaces/IWorker.h
@@ -5,8 +5,8 @@
  * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
  * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
  * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -35,7 +35,6 @@ namespace xmrig {
 
 class VirtualMemory;
 class Job;
-class Config;
 
 
 class IWorker
@@ -49,7 +48,7 @@ public:
     virtual size_t intensity() const                          = 0;
     virtual uint64_t rawHashes() const                        = 0;
     virtual void getHashrateData(uint64_t&, uint64_t&) const  = 0;
-    virtual void start(Config*)                               = 0;
+    virtual void start()                                      = 0;
     virtual void jobEarlyNotification(const Job&)             = 0;
 
 #   ifdef XMRIG_FEATURE_BENCHMARK
diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp
index a847567ae..ecce64fe1 100644
--- a/src/backend/cpu/CpuBackend.cpp
+++ b/src/backend/cpu/CpuBackend.cpp
@@ -390,7 +390,7 @@ void xmrig::CpuBackend::start(IWorker *worker, bool ready)
     mutex.unlock();
 
     if (ready) {
-        worker->start(d_ptr->controller->config());
+        worker->start();
     }
 }
 
diff --git a/src/backend/cpu/CpuConfig.cpp b/src/backend/cpu/CpuConfig.cpp
index 619813d27..5614ef61f 100644
--- a/src/backend/cpu/CpuConfig.cpp
+++ b/src/backend/cpu/CpuConfig.cpp
@@ -116,16 +116,17 @@ size_t xmrig::CpuConfig::memPoolSize() const
 std::vector<xmrig::CpuLaunchData> xmrig::CpuConfig::get(const Miner *miner, const Algorithm &algorithm, uint32_t benchSize) const
 {
     std::vector<CpuLaunchData> out;
-    const CpuThreads &threads = m_threads.get(algorithm);
+    const auto &threads = m_threads.get(algorithm);
 
     if (threads.isEmpty()) {
         return out;
     }
 
-    out.reserve(threads.count());
+    const size_t count = threads.count();
+    out.reserve(count);
 
-    for (const CpuThread &thread : threads.data()) {
-        out.emplace_back(miner, algorithm, *this, thread, benchSize);
+    for (const auto &thread : threads.data()) {
+        out.emplace_back(miner, algorithm, *this, thread, benchSize, count);
     }
 
     return out;
diff --git a/src/backend/cpu/CpuLaunchData.cpp b/src/backend/cpu/CpuLaunchData.cpp
index 369288418..94628c84e 100644
--- a/src/backend/cpu/CpuLaunchData.cpp
+++ b/src/backend/cpu/CpuLaunchData.cpp
@@ -32,7 +32,7 @@
 #include <algorithm>
 
 
-xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, uint32_t benchSize) :
+xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, uint32_t benchSize, size_t threads) :
     algorithm(algorithm),
     assembly(config.assembly()),
     astrobwtAVX2(config.astrobwtAVX2()),
@@ -43,6 +43,7 @@ xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorit
     priority(config.priority()),
     affinity(thread.affinity()),
     miner(miner),
+    threads(threads),
     benchSize(benchSize),
     intensity(std::min<uint32_t>(thread.intensity(), algorithm.maxIntensity()))
 {
diff --git a/src/backend/cpu/CpuLaunchData.h b/src/backend/cpu/CpuLaunchData.h
index 1bc9a7fde..8ba3180f9 100644
--- a/src/backend/cpu/CpuLaunchData.h
+++ b/src/backend/cpu/CpuLaunchData.h
@@ -44,7 +44,7 @@ class Miner;
 class CpuLaunchData
 {
 public:
-    CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, uint32_t benchSize);
+    CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, uint32_t benchSize, size_t threads);
 
     bool isEqual(const CpuLaunchData &other) const;
     CnHash::AlgoVariant av() const;
@@ -66,6 +66,7 @@ public:
     const int priority;
     const int64_t affinity;
     const Miner *miner;
+    const size_t threads;
     const uint32_t benchSize;
     const uint32_t intensity;
 };
diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp
index 4c7781c3c..6aa0b9328 100644
--- a/src/backend/cpu/CpuWorker.cpp
+++ b/src/backend/cpu/CpuWorker.cpp
@@ -86,6 +86,7 @@ xmrig::CpuWorker<N>::CpuWorker(size_t id, const CpuLaunchData &data) :
     m_av(data.av()),
     m_astrobwtMaxSize(data.astrobwtMaxSize * 1000),
     m_miner(data.miner),
+    m_threads(data.threads),
     m_benchSize(data.benchSize),
     m_ctx()
 {
@@ -199,7 +200,7 @@ bool xmrig::CpuWorker<N>::selfTest()
 
 
 template<size_t N>
-void xmrig::CpuWorker<N>::start(xmrig::Config* config)
+void xmrig::CpuWorker<N>::start()
 {
     while (Nonce::sequence(Nonce::CPU) > 0) {
         if (Nonce::isPaused()) {
@@ -220,10 +221,6 @@ void xmrig::CpuWorker<N>::start(xmrig::Config* config)
         alignas(16) uint64_t tempHash[8] = {};
 #       endif
 
-#       ifdef XMRIG_FEATURE_BENCHMARK
-        const size_t benchThreads = config->cpu().threads().get(m_job.currentJob().algorithm()).count();
-#       endif
-
         while (!Nonce::isOutdated(Nonce::CPU, m_job.sequence())) {
             const Job &job = m_job.currentJob();
 
@@ -253,7 +250,7 @@ void xmrig::CpuWorker<N>::start(xmrig::Config* config)
                 }
 
                 // Make each hash dependent on the previous one in single thread benchmark to prevent cheating with multiple threads
-                if (benchThreads == 1) {
+                if (m_threads == 1) {
                     *(uint64_t*)(m_job.blob()) ^= m_benchData;
                 }
             }
diff --git a/src/backend/cpu/CpuWorker.h b/src/backend/cpu/CpuWorker.h
index a43837812..086235fab 100644
--- a/src/backend/cpu/CpuWorker.h
+++ b/src/backend/cpu/CpuWorker.h
@@ -43,7 +43,6 @@ namespace xmrig {
 
 
 class RxVm;
-class Config;
 
 
 template<size_t N>
@@ -57,7 +56,7 @@ public:
 
 protected:
     bool selfTest() override;
-    void start(Config*) override;
+    void start() override;
 
     inline const VirtualMemory *memory() const override { return m_memory; }
     inline size_t intensity() const override            { return N; }
@@ -83,6 +82,7 @@ private:
     const CnHash::AlgoVariant m_av;
     const int m_astrobwtMaxSize;
     const Miner *m_miner;
+    const size_t m_threads;
     const uint32_t m_benchSize;
     cryptonight_ctx *m_ctx[N];
     VirtualMemory *m_memory = nullptr;
diff --git a/src/backend/cuda/CudaBackend.cpp b/src/backend/cuda/CudaBackend.cpp
index f347aacfd..ab5b22e53 100644
--- a/src/backend/cuda/CudaBackend.cpp
+++ b/src/backend/cuda/CudaBackend.cpp
@@ -481,7 +481,7 @@ void xmrig::CudaBackend::start(IWorker *worker, bool ready)
     mutex.unlock();
 
     if (ready) {
-        worker->start(d_ptr->controller->config());
+        worker->start();
     }
 }
 
diff --git a/src/backend/cuda/CudaWorker.cpp b/src/backend/cuda/CudaWorker.cpp
index 9e34e8500..7bb8844c1 100644
--- a/src/backend/cuda/CudaWorker.cpp
+++ b/src/backend/cuda/CudaWorker.cpp
@@ -145,7 +145,7 @@ size_t xmrig::CudaWorker::intensity() const
 }
 
 
-void xmrig::CudaWorker::start(xmrig::Config*)
+void xmrig::CudaWorker::start()
 {
     while (Nonce::sequence(Nonce::CUDA) > 0) {
         if (!isReady()) {
diff --git a/src/backend/cuda/CudaWorker.h b/src/backend/cuda/CudaWorker.h
index 9ccdc519b..e82e34254 100644
--- a/src/backend/cuda/CudaWorker.h
+++ b/src/backend/cuda/CudaWorker.h
@@ -39,7 +39,6 @@ namespace xmrig {
 
 
 class ICudaRunner;
-class Config;
 
 
 class CudaWorker : public Worker
@@ -59,7 +58,7 @@ public:
 protected:
     bool selfTest() override;
     size_t intensity() const override;
-    void start(Config*) override;
+    void start() override;
 
 private:
     bool consumeJob();
diff --git a/src/backend/opencl/OclBackend.cpp b/src/backend/opencl/OclBackend.cpp
index 04bc2cdb5..7b99700b5 100644
--- a/src/backend/opencl/OclBackend.cpp
+++ b/src/backend/opencl/OclBackend.cpp
@@ -463,7 +463,7 @@ void xmrig::OclBackend::start(IWorker *worker, bool ready)
     mutex.unlock();
 
     if (ready) {
-        worker->start(d_ptr->controller->config());
+        worker->start();
     }
 }
 
diff --git a/src/backend/opencl/OclWorker.cpp b/src/backend/opencl/OclWorker.cpp
index ca6cfaccf..ac493ab55 100644
--- a/src/backend/opencl/OclWorker.cpp
+++ b/src/backend/opencl/OclWorker.cpp
@@ -163,7 +163,7 @@ size_t xmrig::OclWorker::intensity() const
 }
 
 
-void xmrig::OclWorker::start(xmrig::Config*)
+void xmrig::OclWorker::start()
 {
     cl_uint results[0x100];
 
diff --git a/src/backend/opencl/OclWorker.h b/src/backend/opencl/OclWorker.h
index d43ccf561..403a0765d 100644
--- a/src/backend/opencl/OclWorker.h
+++ b/src/backend/opencl/OclWorker.h
@@ -40,7 +40,6 @@ namespace xmrig {
 
 class IOclRunner;
 class Job;
-class Config;
 
 
 class OclWorker : public Worker
@@ -60,7 +59,7 @@ public:
 protected:
     bool selfTest() override;
     size_t intensity() const override;
-    void start(Config*) override;
+    void start() override;
 
 private:
     bool consumeJob();

From be8245fc92866c79d771f9c1a2d859584570dbb6 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Mon, 16 Nov 2020 08:56:35 +0700
Subject: [PATCH 4/6] Unlock benchmark size for debug builds.

---
 src/backend/common/Benchmark.cpp               | 12 ++++++++++++
 src/base/net/stratum/benchmark/BenchConfig.cpp |  4 ++++
 2 files changed, 16 insertions(+)

diff --git a/src/backend/common/Benchmark.cpp b/src/backend/common/Benchmark.cpp
index 4e9b696b8..72e8ee9b9 100644
--- a/src/backend/common/Benchmark.cpp
+++ b/src/backend/common/Benchmark.cpp
@@ -39,6 +39,9 @@ namespace xmrig {
 
 static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck = {
     { Algorithm::RX_0, {
+#       ifndef NDEBUG
+        {    10000U, 0x4A597463865ACF0EULL },
+#       endif
         {   250000U, 0x7D6054757BB08A63ULL },
         {   500000U, 0x96607546DE1F5ECCULL },
         {  1000000U, 0x898B6E0431C28A6BULL },
@@ -53,6 +56,9 @@ static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck = {
         { 10000000U, 0xB5231262E2792B26ULL }
     }},
     { Algorithm::RX_WOW, {
+#       ifndef NDEBUG
+        {    10000U, 0x6B0918757100B338ULL },
+#       endif
         {   250000U, 0xC7F712C9603E2603ULL },
         {   500000U, 0x21A0E5AAE6DA7D8DULL },
         {  1000000U, 0x0F3E5400B39EA96AULL },
@@ -71,6 +77,9 @@ static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck = {
 
 static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck1T = {
     { Algorithm::RX_0, {
+#       ifndef NDEBUG
+        {    10000U, 0xADFC3A66F79BFE7FULL },
+#       endif
         {   250000U, 0x90A15B799486F3EBULL },
         {   500000U, 0xAA83118FEE570F9AULL },
         {  1000000U, 0x3DF47B0A427C93D9ULL },
@@ -85,6 +94,9 @@ static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck1T = {
         { 10000000U, 0x95E6BAE68DD779CDULL }
     }},
     { Algorithm::RX_WOW, {
+#       ifndef NDEBUG
+        {    10000U, 0x9EC1B9B8C8C7F082ULL },
+#       endif
         {   250000U, 0x7B409F096C863207ULL },
         {   500000U, 0x70B7B80D15654216ULL },
         {  1000000U, 0x31301CC550306A59ULL },
diff --git a/src/base/net/stratum/benchmark/BenchConfig.cpp b/src/base/net/stratum/benchmark/BenchConfig.cpp
index 8a524108f..2e02755a1 100644
--- a/src/base/net/stratum/benchmark/BenchConfig.cpp
+++ b/src/base/net/stratum/benchmark/BenchConfig.cpp
@@ -137,5 +137,9 @@ uint32_t xmrig::BenchConfig::getSize(const char *benchmark)
         return strcasecmp(benchmark, fmt::format("{}K", size).c_str()) == 0 ? size * 1000 : 0;
     }
 
+#   ifndef NDEBUG
+    return size >= 10000 ? size : 0;
+#   else
     return 0;
+#   endif
 }

From c1d99bfa09fffa2daf81f922c32e9a04886dcc98 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Mon, 16 Nov 2020 16:22:34 +0700
Subject: [PATCH 5/6] Benchmark refactoring, zero delay submit and unified HTTP
 layer.

---
 src/backend/common/Benchmark.cpp              | 289 ------------------
 src/backend/common/Worker.cpp                 |   4 +-
 src/backend/common/Worker.h                   |  15 +-
 src/backend/common/Workers.cpp                |   8 +-
 src/backend/common/benchmark/BenchState.cpp   | 108 +++++++
 src/backend/common/benchmark/BenchState.h     |  53 ++++
 .../common/benchmark/BenchState_test.h        | 112 +++++++
 src/backend/common/benchmark/Benchmark.cpp    |  60 ++++
 .../common/{ => benchmark}/Benchmark.h        |  32 +-
 src/backend/common/common.cmake               |  13 +-
 .../common/interfaces/IBenchListener.h        |  48 +++
 src/backend/common/interfaces/IWorker.h       |  13 +-
 src/backend/cpu/CpuBackend.cpp                |   2 +-
 src/backend/cpu/CpuWorker.cpp                 |   8 +-
 src/backend/cpu/CpuWorker.h                   |   4 +
 src/base/io/Async.cpp                         |  11 +
 src/base/io/Async.h                           |   6 +
 src/base/net/stratum/Job.cpp                  |   8 +-
 src/base/net/stratum/Job.h                    |   6 -
 .../net/stratum/benchmark/BenchClient.cpp     | 116 ++++++-
 src/base/net/stratum/benchmark/BenchClient.h  |  22 +-
 21 files changed, 558 insertions(+), 380 deletions(-)
 delete mode 100644 src/backend/common/Benchmark.cpp
 create mode 100644 src/backend/common/benchmark/BenchState.cpp
 create mode 100644 src/backend/common/benchmark/BenchState.h
 create mode 100644 src/backend/common/benchmark/BenchState_test.h
 create mode 100644 src/backend/common/benchmark/Benchmark.cpp
 rename src/backend/common/{ => benchmark}/Benchmark.h (63%)
 create mode 100644 src/backend/common/interfaces/IBenchListener.h

diff --git a/src/backend/common/Benchmark.cpp b/src/backend/common/Benchmark.cpp
deleted file mode 100644
index 72e8ee9b9..000000000
--- a/src/backend/common/Benchmark.cpp
+++ /dev/null
@@ -1,289 +0,0 @@
-/* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 3 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "backend/common/Benchmark.h"
-#include "3rdparty/fmt/core.h"
-#include "backend/common/interfaces/IBackend.h"
-#include "backend/common/interfaces/IWorker.h"
-#include "base/io/log/Log.h"
-#include "base/io/log/Tags.h"
-#include "base/net/http/Fetch.h"
-#include "base/net/http/HttpData.h"
-#include "base/net/http/HttpListener.h"
-#include "base/net/stratum/benchmark/BenchConfig.h"
-#include "base/net/stratum/Job.h"
-#include "base/tools/Chrono.h"
-
-
-#include <algorithm>
-
-
-namespace xmrig {
-
-
-static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck = {
-    { Algorithm::RX_0, {
-#       ifndef NDEBUG
-        {    10000U, 0x4A597463865ACF0EULL },
-#       endif
-        {   250000U, 0x7D6054757BB08A63ULL },
-        {   500000U, 0x96607546DE1F5ECCULL },
-        {  1000000U, 0x898B6E0431C28A6BULL },
-        {  2000000U, 0xEE9468F8B40926BCULL },
-        {  3000000U, 0xC2BC5D11724813C0ULL },
-        {  4000000U, 0x3A2C7B285B87F941ULL },
-        {  5000000U, 0x3B5BD2C3A16B450EULL },
-        {  6000000U, 0x5CD0602F20C5C7C4ULL },
-        {  7000000U, 0x101DE939474B6812ULL },
-        {  8000000U, 0x52B765A1B156C6ECULL },
-        {  9000000U, 0x323935102AB6B45CULL },
-        { 10000000U, 0xB5231262E2792B26ULL }
-    }},
-    { Algorithm::RX_WOW, {
-#       ifndef NDEBUG
-        {    10000U, 0x6B0918757100B338ULL },
-#       endif
-        {   250000U, 0xC7F712C9603E2603ULL },
-        {   500000U, 0x21A0E5AAE6DA7D8DULL },
-        {  1000000U, 0x0F3E5400B39EA96AULL },
-        {  2000000U, 0x85944CCFA2752D1FULL },
-        {  3000000U, 0x64AFFCAE991811BAULL },
-        {  4000000U, 0x3E4D0B836D3B13BAULL },
-        {  5000000U, 0xEB7417D621271166ULL },
-        {  6000000U, 0x97FFE10C0949FFA5ULL },
-        {  7000000U, 0x84CAC0F8879A4BA1ULL },
-        {  8000000U, 0xA1B79F031DA2459FULL },
-        {  9000000U, 0x9B65226DA873E65DULL },
-        { 10000000U, 0x0F9E00C5A511C200ULL }
-    }}
-};
-
-
-static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck1T = {
-    { Algorithm::RX_0, {
-#       ifndef NDEBUG
-        {    10000U, 0xADFC3A66F79BFE7FULL },
-#       endif
-        {   250000U, 0x90A15B799486F3EBULL },
-        {   500000U, 0xAA83118FEE570F9AULL },
-        {  1000000U, 0x3DF47B0A427C93D9ULL },
-        {  2000000U, 0xED4D639B0AEB85C6ULL },
-        {  3000000U, 0x2D4F9B4275A713C3ULL },
-        {  4000000U, 0xA9EBE4888377F8D3ULL },
-        {  5000000U, 0xB92F81851E180454ULL },
-        {  6000000U, 0xFB9F98F63C2F1B7DULL },
-        {  7000000U, 0x2CC3D7A779D5AB35ULL },
-        {  8000000U, 0x2EEF833EA462F4B1ULL },
-        {  9000000U, 0xC6D39EF59213A07CULL },
-        { 10000000U, 0x95E6BAE68DD779CDULL }
-    }},
-    { Algorithm::RX_WOW, {
-#       ifndef NDEBUG
-        {    10000U, 0x9EC1B9B8C8C7F082ULL },
-#       endif
-        {   250000U, 0x7B409F096C863207ULL },
-        {   500000U, 0x70B7B80D15654216ULL },
-        {  1000000U, 0x31301CC550306A59ULL },
-        {  2000000U, 0x92F65E9E31116361ULL },
-        {  3000000U, 0x7FE8DF6F43BA5285ULL },
-        {  4000000U, 0xD6CDA54FE4D9BBF7ULL },
-        {  5000000U, 0x73AF673E1A38E2B4ULL },
-        {  6000000U, 0x81FDC5C4B45D84E4ULL },
-        {  7000000U, 0xAA08CA57666DC874ULL },
-        {  8000000U, 0x9DCEFB833FC875BCULL },
-        {  9000000U, 0x862F051352CFCA1FULL },
-        { 10000000U, 0xC403F220189E8430ULL }
-    }}
-};
-
-
-} // namespace xmrig
-
-
-xmrig::Benchmark::Benchmark(const Job &job, size_t workers, const IBackend *backend) :
-    m_algo(job.algorithm()),
-    m_backend(backend),
-    m_workers(workers),
-    m_id(job.id()),
-    m_token(job.benchToken()),
-    m_end(job.benchSize()),
-    m_hash(job.benchHash())
-{
-#   ifdef XMRIG_FEATURE_HTTP
-    if (!m_token.isEmpty()) {
-        m_httpListener = std::make_shared<HttpListener>(this, Tags::bench());
-    }
-#   endif
-}
-
-
-bool xmrig::Benchmark::finish(uint64_t totalHashCount)
-{
-    m_reset   = true;
-    m_current = totalHashCount;
-
-    if (m_done < m_workers) {
-        return false;
-    }
-
-    const double dt    = static_cast<double>(m_doneTime - m_startTime) / 1000.0;
-    uint64_t checkData = referenceHash();
-    const char *color  = checkData ? ((m_data == checkData) ? GREEN_BOLD_S : RED_BOLD_S) : BLACK_BOLD_S;
-
-    LOG_NOTICE("%s " WHITE_BOLD("benchmark finished in ") CYAN_BOLD("%.3f seconds") WHITE_BOLD_S " hash sum = " CLEAR "%s%016" PRIX64 CLEAR, Tags::bench(), dt, color, m_data);
-
-#   ifdef XMRIG_FEATURE_HTTP
-    if (!m_token.isEmpty()) {
-        using namespace rapidjson;
-
-        Document doc(kObjectType);
-        auto &allocator = doc.GetAllocator();
-
-        doc.AddMember("steady_done_ts",                 m_doneTime, allocator);
-        doc.AddMember(StringRef(BenchConfig::kHash),    Value(fmt::format("{:016X}", m_data).c_str(), allocator), allocator);
-        doc.AddMember("backend",                        m_backend->toJSON(doc), allocator);
-
-        send(doc);
-    }
-    else
-#   endif
-    {
-        printExit();
-    }
-
-    return true;
-}
-
-
-void xmrig::Benchmark::start()
-{
-    m_startTime = Chrono::steadyMSecs();
-
-#   ifdef XMRIG_FEATURE_HTTP
-    if (!m_token.isEmpty()) {
-        using namespace rapidjson;
-
-        Document doc(kObjectType);
-        doc.AddMember("steady_start_ts", m_startTime, doc.GetAllocator());
-
-        send(doc);
-    }
-#   endif
-}
-
-
-void xmrig::Benchmark::printProgress() const
-{
-    if (!m_startTime || !m_current) {
-        return;
-    }
-
-    const double dt      = static_cast<double>(Chrono::steadyMSecs() - m_startTime) / 1000.0;
-    const double percent = static_cast<double>(m_current) / m_end * 100.0;
-
-    LOG_NOTICE("%s " MAGENTA_BOLD("%5.2f%% ") CYAN_BOLD("%" PRIu64) CYAN("/%" PRIu64) BLACK_BOLD(" (%.3fs)"), Tags::bench(), percent, m_current, m_end, dt);
-}
-
-
-void xmrig::Benchmark::tick(IWorker *worker)
-{
-    if (m_reset) {
-        m_data  = 0;
-        m_done  = 0;
-        m_reset = false;
-    }
-
-    const uint64_t doneTime = worker->benchDoneTime();
-    if (!doneTime) {
-        return;
-    }
-
-    ++m_done;
-    m_data ^= worker->benchData();
-    m_doneTime = std::max(doneTime, m_doneTime);
-}
-
-
-void xmrig::Benchmark::onHttpData(const HttpData &data)
-{
-#   ifdef XMRIG_FEATURE_HTTP
-    rapidjson::Document doc;
-
-    try {
-        doc = data.json();
-    } catch (const std::exception &ex) {
-        return setError(ex.what());
-    }
-
-    if (data.status != 200) {
-        return setError(data.statusName());
-    }
-
-    if (m_doneTime) {
-        LOG_NOTICE("%s " WHITE_BOLD("benchmark submitted ") CYAN_BOLD("https://xmrig.com/benchmark/%s"), Tags::bench(), m_id.data());
-        printExit();
-    }
-#   endif
-}
-
-
-uint64_t xmrig::Benchmark::referenceHash() const
-{
-    if (m_hash) {
-        return m_hash;
-    }
-
-#   ifdef XMRIG_FEATURE_HTTP
-    if (!m_token.isEmpty()) {
-        return 0;
-    }
-#   endif
-
-    uint64_t hash = 0;
-
-    try {
-        const auto& h = (m_workers == 1) ? hashCheck1T : hashCheck;
-        hash = h.at(m_algo).at(m_end);
-    } catch (const std::exception &ex) {}
-
-    return hash;
-}
-
-
-void xmrig::Benchmark::printExit()
-{
-    LOG_INFO("%s " WHITE_BOLD("press ") MAGENTA_BOLD("Ctrl+C") WHITE_BOLD(" to exit"), Tags::bench());
-}
-
-
-#ifdef XMRIG_FEATURE_HTTP
-void xmrig::Benchmark::send(const rapidjson::Value &body)
-{
-    FetchRequest req(HTTP_PATCH, BenchConfig::kApiHost, BenchConfig::kApiPort, fmt::format("/1/benchmark/{}", m_id).c_str(), body, BenchConfig::kApiTLS, true);
-    req.headers.insert({ "Authorization", fmt::format("Bearer {}", m_token)});
-
-    fetch(std::move(req), m_httpListener);
-}
-
-
-void xmrig::Benchmark::setError(const char *message)
-{
-    LOG_ERR("%s " RED("benchmark failed ") RED_BOLD("\"%s\""), Tags::bench(), message);
-}
-#endif
diff --git a/src/backend/common/Worker.cpp b/src/backend/common/Worker.cpp
index b642a25c7..cf244ab3c 100644
--- a/src/backend/common/Worker.cpp
+++ b/src/backend/common/Worker.cpp
@@ -6,8 +6,8 @@
  * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
  * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
  * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/common/Worker.h b/src/backend/common/Worker.h
index ba00fa3f0..23c592b96 100644
--- a/src/backend/common/Worker.h
+++ b/src/backend/common/Worker.h
@@ -6,8 +6,8 @@
  * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
  * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
  * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -28,7 +28,6 @@
 
 
 #include <atomic>
-#include <cstdint>
 
 
 #include "backend/common/interfaces/IWorker.h"
@@ -49,11 +48,6 @@ public:
 
     void getHashrateData(uint64_t& hashCount, uint64_t& timeStamp) const override;
 
-#   ifdef XMRIG_FEATURE_BENCHMARK
-    inline uint64_t benchData() const override            { return m_benchData; }
-    inline uint64_t benchDoneTime() const override        { return m_benchDoneTime; }
-#   endif
-
 protected:
     void storeStats();
 
@@ -64,11 +58,6 @@ protected:
     uint64_t m_count                = 0;
     uint64_t m_hashCount[2]         = {};
     uint64_t m_timestamp[2]         = {};
-
-#   ifdef XMRIG_FEATURE_BENCHMARK
-    uint64_t m_benchData            = 0;
-    uint64_t m_benchDoneTime        = 0;
-#   endif
 };
 
 
diff --git a/src/backend/common/Workers.cpp b/src/backend/common/Workers.cpp
index 4413ece6b..16265b0a5 100644
--- a/src/backend/common/Workers.cpp
+++ b/src/backend/common/Workers.cpp
@@ -47,7 +47,7 @@
 
 
 #ifdef XMRIG_FEATURE_BENCHMARK
-#   include "backend/common/Benchmark.h"
+#   include "backend/common/benchmark/Benchmark.h"
 #endif
 
 
@@ -124,12 +124,6 @@ bool xmrig::Workers<T>::tick(uint64_t)
                 totalAvailable = false;
             }
             totalHashCount += n;
-
-#           ifdef XMRIG_FEATURE_BENCHMARK
-            if (d_ptr->benchmark) {
-                d_ptr->benchmark->tick(worker);
-            }
-#           endif
         }
     }
 
diff --git a/src/backend/common/benchmark/BenchState.cpp b/src/backend/common/benchmark/BenchState.cpp
new file mode 100644
index 000000000..6e64cfca9
--- /dev/null
+++ b/src/backend/common/benchmark/BenchState.cpp
@@ -0,0 +1,108 @@
+/* XMRig
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "backend/common/benchmark/BenchState.h"
+#include "backend/common/benchmark/BenchState_test.h"
+#include "backend/common/interfaces/IBenchListener.h"
+#include "base/io/Async.h"
+#include "base/tools/Chrono.h"
+
+
+#include <algorithm>
+#include <cassert>
+#include <memory>
+#include <mutex>
+
+
+namespace xmrig {
+
+
+static bool done                        = false;
+static std::mutex mutex;
+static std::shared_ptr<Async> async;
+static uint32_t remaining               = 0;
+static uint64_t doneTime                = 0;
+static uint64_t result                  = 0;
+
+
+IBenchListener *BenchState::m_listener  = nullptr;
+
+
+} // namespace xmrig
+
+
+
+bool xmrig::BenchState::isDone()
+{
+    return xmrig::done;
+}
+
+
+uint64_t xmrig::BenchState::referenceHash(const Algorithm &algo, uint32_t size, uint32_t threads)
+{
+    uint64_t hash = 0;
+
+    try {
+        const auto &h = (threads == 1) ? hashCheck1T : hashCheck;
+        hash = h.at(algo).at(size);
+    } catch (const std::exception &ex) {}
+
+    return hash;
+}
+
+
+uint64_t xmrig::BenchState::start(size_t threads, const IBackend *backend)
+{
+    assert(m_listener != nullptr);
+
+    remaining = static_cast<uint32_t>(threads);
+
+    async = std::make_shared<Async>([] {
+        m_listener->onBenchDone(result, doneTime);
+        async.reset();
+        xmrig::done = true;
+    });
+
+    const uint64_t ts = Chrono::steadyMSecs();
+    m_listener->onBenchStart(ts, remaining, backend);
+
+    return ts;
+}
+
+
+void xmrig::BenchState::destroy()
+{
+    async.reset();
+}
+
+
+void xmrig::BenchState::done(uint64_t data, uint64_t ts)
+{
+    assert(async && remaining > 0);
+
+    std::lock_guard<std::mutex> lock(mutex);
+
+    result ^= data;
+    doneTime = std::max(doneTime, ts);
+    --remaining;
+
+    if (remaining == 0) {
+        async->send();
+    }
+}
diff --git a/src/backend/common/benchmark/BenchState.h b/src/backend/common/benchmark/BenchState.h
new file mode 100644
index 000000000..4f65ac177
--- /dev/null
+++ b/src/backend/common/benchmark/BenchState.h
@@ -0,0 +1,53 @@
+/* XMRig
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef XMRIG_BENCHSTATE_H
+#define XMRIG_BENCHSTATE_H
+
+
+#include <cstdint>
+
+
+namespace xmrig {
+
+
+class Algorithm;
+class IBackend;
+class IBenchListener;
+
+
+class BenchState
+{
+public:
+    static bool isDone();
+    static uint64_t referenceHash(const Algorithm &algo, uint32_t size, uint32_t threads);
+    static uint64_t start(size_t threads, const IBackend *backend);
+    static void destroy();
+    static void done(uint64_t data, uint64_t ts);
+
+    inline static void setListener(IBenchListener *listener)    { m_listener = listener; }
+
+private:
+    static IBenchListener *m_listener;
+};
+
+
+} // namespace xmrig
+
+
+#endif /* XMRIG_BENCHSTATE_H */
diff --git a/src/backend/common/benchmark/BenchState_test.h b/src/backend/common/benchmark/BenchState_test.h
new file mode 100644
index 000000000..0e59a7457
--- /dev/null
+++ b/src/backend/common/benchmark/BenchState_test.h
@@ -0,0 +1,112 @@
+/* XMRig
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef XMRIG_BENCHSTATE_TEST_H
+#define XMRIG_BENCHSTATE_TEST_H
+
+
+#include "base/crypto/Algorithm.h"
+
+
+#include <map>
+
+
+namespace xmrig {
+
+
+static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck = {
+    { Algorithm::RX_0, {
+#       ifndef NDEBUG
+        {    10000U, 0x4A597463865ACF0EULL },
+#       endif
+        {   250000U, 0x7D6054757BB08A63ULL },
+        {   500000U, 0x96607546DE1F5ECCULL },
+        {  1000000U, 0x898B6E0431C28A6BULL },
+        {  2000000U, 0xEE9468F8B40926BCULL },
+        {  3000000U, 0xC2BC5D11724813C0ULL },
+        {  4000000U, 0x3A2C7B285B87F941ULL },
+        {  5000000U, 0x3B5BD2C3A16B450EULL },
+        {  6000000U, 0x5CD0602F20C5C7C4ULL },
+        {  7000000U, 0x101DE939474B6812ULL },
+        {  8000000U, 0x52B765A1B156C6ECULL },
+        {  9000000U, 0x323935102AB6B45CULL },
+        { 10000000U, 0xB5231262E2792B26ULL }
+    }},
+    { Algorithm::RX_WOW, {
+#       ifndef NDEBUG
+        {    10000U, 0x6B0918757100B338ULL },
+#       endif
+        {   250000U, 0xC7F712C9603E2603ULL },
+        {   500000U, 0x21A0E5AAE6DA7D8DULL },
+        {  1000000U, 0x0F3E5400B39EA96AULL },
+        {  2000000U, 0x85944CCFA2752D1FULL },
+        {  3000000U, 0x64AFFCAE991811BAULL },
+        {  4000000U, 0x3E4D0B836D3B13BAULL },
+        {  5000000U, 0xEB7417D621271166ULL },
+        {  6000000U, 0x97FFE10C0949FFA5ULL },
+        {  7000000U, 0x84CAC0F8879A4BA1ULL },
+        {  8000000U, 0xA1B79F031DA2459FULL },
+        {  9000000U, 0x9B65226DA873E65DULL },
+        { 10000000U, 0x0F9E00C5A511C200ULL }
+    }}
+};
+
+
+static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck1T = {
+    { Algorithm::RX_0, {
+#       ifndef NDEBUG
+        {    10000U, 0xADFC3A66F79BFE7FULL },
+#       endif
+        {   250000U, 0x90A15B799486F3EBULL },
+        {   500000U, 0xAA83118FEE570F9AULL },
+        {  1000000U, 0x3DF47B0A427C93D9ULL },
+        {  2000000U, 0xED4D639B0AEB85C6ULL },
+        {  3000000U, 0x2D4F9B4275A713C3ULL },
+        {  4000000U, 0xA9EBE4888377F8D3ULL },
+        {  5000000U, 0xB92F81851E180454ULL },
+        {  6000000U, 0xFB9F98F63C2F1B7DULL },
+        {  7000000U, 0x2CC3D7A779D5AB35ULL },
+        {  8000000U, 0x2EEF833EA462F4B1ULL },
+        {  9000000U, 0xC6D39EF59213A07CULL },
+        { 10000000U, 0x95E6BAE68DD779CDULL }
+    }},
+    { Algorithm::RX_WOW, {
+#       ifndef NDEBUG
+        {    10000U, 0x9EC1B9B8C8C7F082ULL },
+#       endif
+        {   250000U, 0x7B409F096C863207ULL },
+        {   500000U, 0x70B7B80D15654216ULL },
+        {  1000000U, 0x31301CC550306A59ULL },
+        {  2000000U, 0x92F65E9E31116361ULL },
+        {  3000000U, 0x7FE8DF6F43BA5285ULL },
+        {  4000000U, 0xD6CDA54FE4D9BBF7ULL },
+        {  5000000U, 0x73AF673E1A38E2B4ULL },
+        {  6000000U, 0x81FDC5C4B45D84E4ULL },
+        {  7000000U, 0xAA08CA57666DC874ULL },
+        {  8000000U, 0x9DCEFB833FC875BCULL },
+        {  9000000U, 0x862F051352CFCA1FULL },
+        { 10000000U, 0xC403F220189E8430ULL }
+    }}
+};
+
+
+} // namespace xmrig
+
+
+
+#endif /* XMRIG_BENCHSTATE_TEST_H */
diff --git a/src/backend/common/benchmark/Benchmark.cpp b/src/backend/common/benchmark/Benchmark.cpp
new file mode 100644
index 000000000..1e8e3a351
--- /dev/null
+++ b/src/backend/common/benchmark/Benchmark.cpp
@@ -0,0 +1,60 @@
+/* XMRig
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "backend/common/benchmark/Benchmark.h"
+#include "backend/common/benchmark/BenchState.h"
+#include "base/io/log/Log.h"
+#include "base/io/log/Tags.h"
+#include "base/net/stratum/Job.h"
+#include "base/tools/Chrono.h"
+
+
+xmrig::Benchmark::Benchmark(const Job &job, size_t workers, const IBackend *backend) :
+    m_backend(backend),
+    m_workers(workers),
+    m_end(job.benchSize())
+{
+}
+
+
+bool xmrig::Benchmark::finish(uint64_t totalHashCount)
+{
+    m_current = totalHashCount;
+
+    return BenchState::isDone();
+}
+
+
+void xmrig::Benchmark::start()
+{
+    m_startTime = BenchState::start(m_workers, m_backend);
+}
+
+
+void xmrig::Benchmark::printProgress() const
+{
+    if (!m_startTime || !m_current) {
+        return;
+    }
+
+    const double dt      = static_cast<double>(Chrono::steadyMSecs() - m_startTime) / 1000.0;
+    const double percent = static_cast<double>(m_current) / m_end * 100.0;
+
+    LOG_NOTICE("%s " MAGENTA_BOLD("%5.2f%% ") CYAN_BOLD("%" PRIu64) CYAN("/%" PRIu64) BLACK_BOLD(" (%.3fs)"), Tags::bench(), percent, m_current, m_end, dt);
+}
diff --git a/src/backend/common/Benchmark.h b/src/backend/common/benchmark/Benchmark.h
similarity index 63%
rename from src/backend/common/Benchmark.h
rename to src/backend/common/benchmark/Benchmark.h
index b13748b46..b558cd1a5 100644
--- a/src/backend/common/Benchmark.h
+++ b/src/backend/common/benchmark/Benchmark.h
@@ -20,61 +20,33 @@
 #define XMRIG_BENCHMARK_H
 
 
-#include "base/crypto/Algorithm.h"
-#include "base/kernel/interfaces/IHttpListener.h"
 #include "base/tools/Object.h"
-#include "base/tools/String.h"
-
-
-#include <memory>
 
 
 namespace xmrig {
 
 
 class IBackend;
-class IWorker;
 class Job;
 
 
-class Benchmark : public IHttpListener
+class Benchmark
 {
 public:
     XMRIG_DISABLE_COPY_MOVE_DEFAULT(Benchmark)
 
     Benchmark(const Job &job, size_t workers, const IBackend *backend);
-    ~Benchmark() override = default;
+    ~Benchmark() = default;
 
     bool finish(uint64_t totalHashCount);
     void printProgress() const;
     void start();
-    void tick(IWorker *worker);
-
-protected:
-    void onHttpData(const HttpData &data) override;
 
 private:
-    uint64_t referenceHash() const;
-    void printExit();
-
-#   ifdef XMRIG_FEATURE_HTTP
-    void send(const rapidjson::Value &body);
-    void setError(const char *message);
-#   endif
-
-    bool m_reset                = false;
-    const Algorithm m_algo;
     const IBackend *m_backend;
     const size_t m_workers;
-    const String m_id;
-    const String m_token;
     const uint64_t m_end;
-    const uint64_t m_hash;
-    std::shared_ptr<IHttpListener> m_httpListener;
-    uint32_t m_done             = 0;
     uint64_t m_current          = 0;
-    uint64_t m_data             = 0;
-    uint64_t m_doneTime         = 0;
     uint64_t m_startTime        = 0;
 };
 
diff --git a/src/backend/common/common.cmake b/src/backend/common/common.cmake
index eb1109339..95c724405 100644
--- a/src/backend/common/common.cmake
+++ b/src/backend/common/common.cmake
@@ -23,6 +23,15 @@ set(SOURCES_BACKEND_COMMON
    )
 
 if (WITH_RANDOMX AND WITH_BENCHMARK)
-    list(APPEND HEADERS_BACKEND_COMMON src/backend/common/Benchmark.h)
-    list(APPEND SOURCES_BACKEND_COMMON src/backend/common/Benchmark.cpp)
+    list(APPEND HEADERS_BACKEND_COMMON
+        src/backend/common/benchmark/Benchmark.h
+        src/backend/common/benchmark/BenchState_test.h
+        src/backend/common/benchmark/BenchState.h
+        src/backend/common/interfaces/IBenchListener.h
+        )
+
+    list(APPEND SOURCES_BACKEND_COMMON
+        src/backend/common/benchmark/Benchmark.cpp
+        src/backend/common/benchmark/BenchState.cpp
+        )
 endif()
diff --git a/src/backend/common/interfaces/IBenchListener.h b/src/backend/common/interfaces/IBenchListener.h
new file mode 100644
index 000000000..63575b8b7
--- /dev/null
+++ b/src/backend/common/interfaces/IBenchListener.h
@@ -0,0 +1,48 @@
+/* XMRig
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef XMRIG_IBENCHLISTENER_H
+#define XMRIG_IBENCHLISTENER_H
+
+
+#include "base/tools/Object.h"
+
+
+namespace xmrig {
+
+
+class IBackend;
+
+
+class IBenchListener
+{
+public:
+    XMRIG_DISABLE_COPY_MOVE(IBenchListener)
+
+    IBenchListener()            = default;
+    virtual ~IBenchListener()   = default;
+
+    virtual void onBenchDone(uint64_t result, uint64_t ts)                              = 0;
+    virtual void onBenchStart(uint64_t ts, uint32_t threads, const IBackend *backend)   = 0;
+};
+
+
+} /* namespace xmrig */
+
+
+#endif // XMRIG_IBENCHLISTENER_H
diff --git a/src/backend/common/interfaces/IWorker.h b/src/backend/common/interfaces/IWorker.h
index b37afcaf8..f528469a5 100644
--- a/src/backend/common/interfaces/IWorker.h
+++ b/src/backend/common/interfaces/IWorker.h
@@ -26,6 +26,9 @@
 #define XMRIG_IWORKER_H
 
 
+#include "base/tools/Object.h"
+
+
 #include <cstdint>
 #include <cstddef>
 
@@ -40,7 +43,10 @@ class Job;
 class IWorker
 {
 public:
-    virtual ~IWorker() = default;
+    XMRIG_DISABLE_COPY_MOVE(IWorker)
+
+    IWorker()           = default;
+    virtual ~IWorker()  = default;
 
     virtual bool selfTest()                                   = 0;
     virtual const VirtualMemory *memory() const               = 0;
@@ -50,11 +56,6 @@ public:
     virtual void getHashrateData(uint64_t&, uint64_t&) const  = 0;
     virtual void start()                                      = 0;
     virtual void jobEarlyNotification(const Job&)             = 0;
-
-#   ifdef XMRIG_FEATURE_BENCHMARK
-    virtual uint64_t benchData() const                        = 0;
-    virtual uint64_t benchDoneTime() const                    = 0;
-#   endif
 };
 
 
diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp
index ecce64fe1..f5ff86acd 100644
--- a/src/backend/cpu/CpuBackend.cpp
+++ b/src/backend/cpu/CpuBackend.cpp
@@ -56,7 +56,7 @@
 
 
 #ifdef XMRIG_FEATURE_BENCHMARK
-#   include "backend/common/Benchmark.h"
+#   include "backend/common/benchmark/Benchmark.h"
 #endif
 
 
diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp
index 6aa0b9328..545754220 100644
--- a/src/backend/cpu/CpuWorker.cpp
+++ b/src/backend/cpu/CpuWorker.cpp
@@ -53,6 +53,11 @@
 #endif
 
 
+#ifdef XMRIG_FEATURE_BENCHMARK
+#   include "backend/common/benchmark/BenchState.h"
+#endif
+
+
 namespace xmrig {
 
 static constexpr uint32_t kReserveCount = 32768;
@@ -245,8 +250,7 @@ void xmrig::CpuWorker<N>::start()
 
                 // Stop benchmark when all hashes have been counted
                 if (done) {
-                    m_benchDoneTime = Chrono::steadyMSecs();
-                    return;
+                    return BenchState::done(m_benchData, Chrono::steadyMSecs());;
                 }
 
                 // Make each hash dependent on the previous one in single thread benchmark to prevent cheating with multiple threads
diff --git a/src/backend/cpu/CpuWorker.h b/src/backend/cpu/CpuWorker.h
index 086235fab..640cba520 100644
--- a/src/backend/cpu/CpuWorker.h
+++ b/src/backend/cpu/CpuWorker.h
@@ -91,6 +91,10 @@ private:
 #   ifdef XMRIG_ALGO_RANDOMX
     randomx_vm *m_vm = nullptr;
 #   endif
+
+#   ifdef XMRIG_FEATURE_BENCHMARK
+    uint64_t m_benchData    = 0;
+#   endif
 };
 
 
diff --git a/src/base/io/Async.cpp b/src/base/io/Async.cpp
index 5ee637f2c..255be8c0f 100644
--- a/src/base/io/Async.cpp
+++ b/src/base/io/Async.cpp
@@ -119,6 +119,7 @@ namespace xmrig {
 class AsyncPrivate
 {
 public:
+    Async::Callback callback;
     IAsyncListener *listener    = nullptr;
     uv_async_t *async           = nullptr;
 };
@@ -127,6 +128,16 @@ public:
 } // namespace xmrig
 
 
+xmrig::Async::Async(Callback callback) : d_ptr(new AsyncPrivate())
+{
+    d_ptr->callback     = std::move(callback);
+    d_ptr->async        = new uv_async_t;
+    d_ptr->async->data  = this;
+
+    uv_async_init(uv_default_loop(), d_ptr->async, [](uv_async_t *handle) { static_cast<Async *>(handle->data)->d_ptr->callback(); });
+}
+
+
 xmrig::Async::Async(IAsyncListener *listener) : d_ptr(new AsyncPrivate())
 {
     d_ptr->listener     = listener;
diff --git a/src/base/io/Async.h b/src/base/io/Async.h
index 4b8d1a7bf..19491082b 100644
--- a/src/base/io/Async.h
+++ b/src/base/io/Async.h
@@ -25,6 +25,9 @@
 #include "base/tools/Object.h"
 
 
+#include <functional>
+
+
 namespace xmrig {
 
 
@@ -37,6 +40,9 @@ class Async
 public:
     XMRIG_DISABLE_COPY_MOVE_DEFAULT(Async)
 
+    using Callback = std::function<void()>;
+
+    Async(Callback callback);
     Async(IAsyncListener *listener);
     ~Async();
 
diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp
index f791a9568..203454143 100644
--- a/src/base/net/stratum/Job.cpp
+++ b/src/base/net/stratum/Job.cpp
@@ -176,9 +176,7 @@ void xmrig::Job::copy(const Job &other)
 #   endif
 
 #   ifdef XMRIG_FEATURE_BENCHMARK
-    m_benchSize  = other.m_benchSize;
-    m_benchHash  = other.m_benchHash;
-    m_benchToken = other.m_benchToken;
+    m_benchSize = other.m_benchSize;
 #   endif
 }
 
@@ -213,8 +211,6 @@ void xmrig::Job::move(Job &&other)
 #   endif
 
 #   ifdef XMRIG_FEATURE_BENCHMARK
-    m_benchSize  = other.m_benchSize;
-    m_benchHash  = other.m_benchHash;
-    m_benchToken = std::move(other.m_benchToken);
+    m_benchSize = other.m_benchSize;
 #   endif
 }
diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h
index ac564d37b..5621cbc7f 100644
--- a/src/base/net/stratum/Job.h
+++ b/src/base/net/stratum/Job.h
@@ -112,12 +112,8 @@ public:
     inline Job &operator=(Job &&other) noexcept         { move(std::move(other)); return *this; }
 
 #   ifdef XMRIG_FEATURE_BENCHMARK
-    inline const String &benchToken() const             { return m_benchToken; }
     inline uint32_t benchSize() const                   { return m_benchSize; }
-    inline uint64_t benchHash() const                   { return m_benchHash; }
-    inline void setBenchHash(uint64_t benchHash)        { m_benchHash = benchHash; }
     inline void setBenchSize(uint32_t size)             { m_benchSize = size; }
-    inline void setBenchToken(const String &token)      { m_benchToken = token; }
 #   endif
 
 private:
@@ -146,9 +142,7 @@ private:
 #   endif
 
 #   ifdef XMRIG_FEATURE_BENCHMARK
-    String m_benchToken;
     uint32_t m_benchSize = 0;
-    uint64_t m_benchHash = 0;
 #   endif
 };
 
diff --git a/src/base/net/stratum/benchmark/BenchClient.cpp b/src/base/net/stratum/benchmark/BenchClient.cpp
index bc71492b1..2bfc868f1 100644
--- a/src/base/net/stratum/benchmark/BenchClient.cpp
+++ b/src/base/net/stratum/benchmark/BenchClient.cpp
@@ -19,6 +19,8 @@
 #include "base/net/stratum/benchmark/BenchClient.h"
 #include "3rdparty/fmt/core.h"
 #include "3rdparty/rapidjson/document.h"
+#include "backend/common/benchmark/BenchState.h"
+#include "backend/common/interfaces/IBackend.h"
 #include "backend/cpu/Cpu.h"
 #include "base/io/json/Json.h"
 #include "base/io/log/Log.h"
@@ -33,7 +35,8 @@
 
 xmrig::BenchClient::BenchClient(const std::shared_ptr<BenchConfig> &benchmark, IClientListener* listener) :
     m_listener(listener),
-    m_benchmark(benchmark)
+    m_benchmark(benchmark),
+    m_hash(benchmark->hash())
 {
     std::vector<char> blob(112 * 2 + 1, '0');
     blob.back() = '\0';
@@ -43,7 +46,8 @@ xmrig::BenchClient::BenchClient(const std::shared_ptr<BenchConfig> &benchmark, I
     m_job.setDiff(std::numeric_limits<uint64_t>::max());
     m_job.setHeight(1);
     m_job.setBenchSize(m_benchmark->size());
-    m_job.setBenchHash(m_benchmark->hash());
+
+    BenchState::setListener(this);
 
 #   ifdef XMRIG_FEATURE_HTTP
     if (m_benchmark->isSubmit()) {
@@ -54,8 +58,8 @@ xmrig::BenchClient::BenchClient(const std::shared_ptr<BenchConfig> &benchmark, I
 
     if (!m_benchmark->id().isEmpty()) {
         m_job.setId(m_benchmark->id());
-        m_job.setBenchToken(m_benchmark->token());
-        m_mode = ONLINE_VERIFY;
+        m_token = m_benchmark->token();
+        m_mode  = ONLINE_VERIFY;
 
         return;
     }
@@ -63,7 +67,7 @@ xmrig::BenchClient::BenchClient(const std::shared_ptr<BenchConfig> &benchmark, I
 
     m_job.setId("00000000");
 
-    if (m_job.benchHash() && m_job.setSeedHash(m_benchmark->seed())) {
+    if (m_hash && m_job.setSeedHash(m_benchmark->seed())) {
         m_mode = STATIC_VERIFY;
 
         return;
@@ -74,6 +78,12 @@ xmrig::BenchClient::BenchClient(const std::shared_ptr<BenchConfig> &benchmark, I
 }
 
 
+xmrig::BenchClient::~BenchClient()
+{
+    BenchState::destroy();
+}
+
+
 void xmrig::BenchClient::connect()
 {
 #   ifdef XMRIG_FEATURE_HTTP
@@ -100,6 +110,54 @@ void xmrig::BenchClient::setPool(const Pool &pool)
 }
 
 
+void xmrig::BenchClient::onBenchDone(uint64_t result, uint64_t ts)
+{
+#   ifdef XMRIG_FEATURE_HTTP
+    if (m_mode == ONLINE_BENCH) {
+        m_doneTime = ts;
+
+        rapidjson::Document doc(rapidjson::kObjectType);
+        auto &allocator = doc.GetAllocator();
+
+        doc.AddMember("steady_done_ts", m_doneTime, allocator);
+        doc.AddMember("hash",           rapidjson::Value(fmt::format("{:016X}", result).c_str(), allocator), allocator);
+        doc.AddMember("backend",        m_backend->toJSON(doc), allocator);
+
+        update(doc);
+    }
+#   endif
+
+    const uint64_t ref = referenceHash();
+    const char *color  = ref ? ((result == ref) ? GREEN_BOLD_S : RED_BOLD_S) : BLACK_BOLD_S;
+
+    LOG_NOTICE("%s " WHITE_BOLD("benchmark finished in ") CYAN_BOLD("%.3f seconds") WHITE_BOLD_S " hash sum = " CLEAR "%s%016" PRIX64 CLEAR, Tags::bench(), static_cast<double>(ts - m_startTime) / 1000.0, color, result);
+
+    if (m_mode != ONLINE_BENCH) {
+        printExit();
+    }
+}
+
+
+void xmrig::BenchClient::onBenchStart(uint64_t ts, uint32_t threads, const IBackend *backend)
+{
+    m_startTime = ts;
+    m_threads   = threads;
+    m_backend   = backend;
+
+#   ifdef XMRIG_FEATURE_HTTP
+    if (m_mode == ONLINE_BENCH) {
+        rapidjson::Document doc(rapidjson::kObjectType);
+        auto &allocator = doc.GetAllocator();
+
+        doc.AddMember("threads",            threads, allocator);
+        doc.AddMember("steady_start_ts",    m_startTime, allocator);
+
+        update(doc);
+    }
+#   endif
+}
+
+
 void xmrig::BenchClient::onHttpData(const HttpData &data)
 {
 #   ifdef XMRIG_FEATURE_HTTP
@@ -116,15 +174,39 @@ void xmrig::BenchClient::onHttpData(const HttpData &data)
     }
 
     if (m_mode == ONLINE_BENCH) {
-        startBench(doc);
-    }
-    else {
-        startVerify(doc);
+        if (!m_startTime) {
+            return startBench(doc);
+        }
+
+        if (m_doneTime) {
+            LOG_NOTICE("%s " WHITE_BOLD("benchmark submitted ") CYAN_BOLD("https://xmrig.com/benchmark/%s"), Tags::bench(), m_job.id().data());
+            printExit();
+        }
+
+        return;
     }
+
+    startVerify(doc);
 #   endif
 }
 
 
+uint64_t xmrig::BenchClient::referenceHash() const
+{
+    if (m_hash || m_mode == ONLINE_BENCH) {
+        return m_hash;
+    }
+
+    return BenchState::referenceHash(m_job.algorithm(), m_job.benchSize(), m_threads);
+}
+
+
+void xmrig::BenchClient::printExit()
+{
+    LOG_INFO("%s " WHITE_BOLD("press ") MAGENTA_BOLD("Ctrl+C") WHITE_BOLD(" to exit"), Tags::bench());
+}
+
+
 void xmrig::BenchClient::start()
 {
     m_listener->onLoginSuccess(this);
@@ -180,7 +262,8 @@ void xmrig::BenchClient::startBench(const rapidjson::Value &value)
 {
     m_job.setId(Json::getString(value, BenchConfig::kId));
     m_job.setSeedHash(Json::getString(value, BenchConfig::kSeed));
-    m_job.setBenchToken(Json::getString(value, BenchConfig::kToken));
+
+    m_token = Json::getString(value, BenchConfig::kToken);
 
     start();
 }
@@ -190,7 +273,7 @@ void xmrig::BenchClient::startVerify(const rapidjson::Value &value)
 {
     const char *hash = Json::getString(value, BenchConfig::kHash);
     if (hash) {
-        m_job.setBenchHash(strtoull(hash, nullptr, 16));
+        m_hash = strtoull(hash, nullptr, 16);
     }
 
     m_job.setAlgorithm(Json::getString(value, BenchConfig::kAlgo));
@@ -199,4 +282,15 @@ void xmrig::BenchClient::startVerify(const rapidjson::Value &value)
 
     start();
 }
+
+
+void xmrig::BenchClient::update(const rapidjson::Value &body)
+{
+    assert(!m_token.isEmpty());
+
+    FetchRequest req(HTTP_PATCH, BenchConfig::kApiHost, BenchConfig::kApiPort, fmt::format("/1/benchmark/{}", m_job.id()).c_str(), body, BenchConfig::kApiTLS, true);
+    req.headers.insert({ "Authorization", fmt::format("Bearer {}", m_token)});
+
+    fetch(std::move(req), m_httpListener);
+}
 #endif
diff --git a/src/base/net/stratum/benchmark/BenchClient.h b/src/base/net/stratum/benchmark/BenchClient.h
index 64bb481b8..34c415c6e 100644
--- a/src/base/net/stratum/benchmark/BenchClient.h
+++ b/src/base/net/stratum/benchmark/BenchClient.h
@@ -20,20 +20,21 @@
 #define XMRIG_BENCHCLIENT_H
 
 
-#include "base/net/stratum/Client.h"
+#include "backend/common/interfaces/IBenchListener.h"
 #include "base/kernel/interfaces/IHttpListener.h"
+#include "base/net/stratum/Client.h"
 
 
 namespace xmrig {
 
 
-class BenchClient : public IClient, public IHttpListener
+class BenchClient : public IClient, public IHttpListener, public IBenchListener
 {
 public:
     XMRIG_DISABLE_COPY_MOVE_DEFAULT(BenchClient)
 
     BenchClient(const std::shared_ptr<BenchConfig> &benchmark, IClientListener* listener);
-    ~BenchClient() override = default;
+    ~BenchClient() override;
 
     inline bool disconnect() override                                               { return true; }
     inline bool hasExtension(Extension) const noexcept override                     { return false; }
@@ -52,7 +53,7 @@ public:
     inline int64_t sequence() const override                                        { return 0; }
     inline int64_t submit(const JobResult &) override                               { return 0; }
     inline void connect(const Pool &pool) override                                  { setPool(pool); }
-    inline void deleteLater() override                                              {}
+    inline void deleteLater() override                                              { delete this; }
     inline void setAlgo(const Algorithm &algo) override                             {}
     inline void setEnabled(bool enabled) override                                   {}
     inline void setProxy(const ProxyUrl &proxy) override                            {}
@@ -65,6 +66,8 @@ public:
     void setPool(const Pool &pool) override;
 
 protected:
+    void onBenchDone(uint64_t result, uint64_t ts) override;
+    void onBenchStart(uint64_t ts, uint32_t threads, const IBackend *backend) override;
     void onHttpData(const HttpData &data) override;
 
 private:
@@ -75,6 +78,8 @@ private:
         ONLINE_VERIFY
     };
 
+    uint64_t referenceHash() const;
+    void printExit();
     void start();
 
 #   ifdef XMRIG_FEATURE_HTTP
@@ -84,15 +89,22 @@ private:
     void setError(const char *message);
     void startBench(const rapidjson::Value &value);
     void startVerify(const rapidjson::Value &value);
+    void update(const rapidjson::Value &body);
 #   endif
 
+    const IBackend *m_backend   = nullptr;
     IClientListener* m_listener;
     Job m_job;
+    Mode m_mode                 = STATIC_BENCH;
     Pool m_pool;
     std::shared_ptr<BenchConfig> m_benchmark;
     std::shared_ptr<IHttpListener> m_httpListener;
     String m_ip;
-    Mode m_mode = STATIC_BENCH;
+    String m_token;
+    uint32_t m_threads          = 0;
+    uint64_t m_doneTime         = 0;
+    uint64_t m_hash             = 0;
+    uint64_t m_startTime        = 0;
 };
 
 

From f7f07ce42c2302cfbbdd5db380f2907b2e8cd7c9 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Mon, 16 Nov 2020 16:37:57 +0700
Subject: [PATCH 6/6] Fixed build.

---
 src/backend/common/benchmark/BenchState.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/backend/common/benchmark/BenchState.h b/src/backend/common/benchmark/BenchState.h
index 4f65ac177..035d12338 100644
--- a/src/backend/common/benchmark/BenchState.h
+++ b/src/backend/common/benchmark/BenchState.h
@@ -20,6 +20,7 @@
 #define XMRIG_BENCHSTATE_H
 
 
+#include <cstddef>
 #include <cstdint>