From 7177b42903af6856ff06bee9be2850d6a92dcb96 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sat, 3 Jul 2021 16:00:30 +0700
Subject: [PATCH 1/8] v6.13.2-dev

---
 src/version.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/version.h b/src/version.h
index 4d7714fdd..5dfc7b2b7 100644
--- a/src/version.h
+++ b/src/version.h
@@ -28,7 +28,7 @@
 #define APP_ID        "xmrig"
 #define APP_NAME      "XMRig"
 #define APP_DESC      "XMRig miner"
-#define APP_VERSION   "6.13.1"
+#define APP_VERSION   "6.13.2-dev"
 #define APP_DOMAIN    "xmrig.com"
 #define APP_SITE      "www.xmrig.com"
 #define APP_COPYRIGHT "Copyright (C) 2016-2021 xmrig.com"
@@ -36,7 +36,7 @@
 
 #define APP_VER_MAJOR  6
 #define APP_VER_MINOR  13
-#define APP_VER_PATCH  1
+#define APP_VER_PATCH  2
 
 #ifdef _MSC_VER
 #   if (_MSC_VER >= 1920)

From 3f2dfa42794fc7a097fa55d426bdfa4f0dc22fdd Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Mon, 5 Jul 2021 02:31:29 +0700
Subject: [PATCH 2/8] Sync with proxy.

---
 src/base/net/stratum/Client.cpp | 15 ++++-----
 src/base/net/stratum/Client.h   |  4 +--
 src/base/net/stratum/Job.cpp    | 60 ++++++++++++++++++++++-----------
 src/base/net/stratum/Job.h      | 23 +++++++------
 4 files changed, 63 insertions(+), 39 deletions(-)

diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp
index 1871b6df0..95e1d8d05 100644
--- a/src/base/net/stratum/Client.cpp
+++ b/src/base/net/stratum/Client.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 2019      jtgrassie   <https://github.com/jtgrassie>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2021 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
@@ -225,6 +225,10 @@ int64_t xmrig::Client::submit(const JobResult &result)
     if (result.minerSignature()) {
         params.AddMember("sig", StringRef(signature), allocator);
     }
+#   else
+    if (result.sig) {
+        params.AddMember("sig", StringRef(result.sig), allocator);
+    }
 #   endif
 
     if (has<EXT_ALGO>() && result.algorithm.isValid()) {
@@ -440,12 +444,7 @@ bool xmrig::Client::parseJob(const rapidjson::Value &params, int *code)
         return false;
     }
 
-#   ifndef XMRIG_PROXY_PROJECT
-    uint8_t signatureKeyBuf[32 * 2];
-    if (Cvt::fromHex(signatureKeyBuf, sizeof(signatureKeyBuf), Json::getValue(params, "sig_key"))) {
-        job.setEphemeralKeys(signatureKeyBuf, signatureKeyBuf + 32);
-    }
-#   endif
+    job.setSigKey(Json::getString(params, "sig_key"));
 
     m_job.setClientId(m_rpcId);
 
diff --git a/src/base/net/stratum/Client.h b/src/base/net/stratum/Client.h
index fdaac9f15..d9164b8a3 100644
--- a/src/base/net/stratum/Client.h
+++ b/src/base/net/stratum/Client.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 2019      jtgrassie   <https://github.com/jtgrassie>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2021 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/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp
index c92f6f015..5e0c0f489 100644
--- a/src/base/net/stratum/Job.cpp
+++ b/src/base/net/stratum/Job.cpp
@@ -7,8 +7,8 @@
  * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
  * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
  * Copyright 2019      Howard Chu  <https://github.com/hyc>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2021 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
@@ -144,6 +144,25 @@ void xmrig::Job::setDiff(uint64_t diff)
 }
 
 
+void xmrig::Job::setSigKey(const char *sig_key)
+{
+    constexpr const size_t size = 64;
+
+    if (!sig_key || strlen(sig_key) != size * 2) {
+        return;
+    }
+
+#   ifndef XMRIG_PROXY_PROJECT
+    const auto buf = Cvt::fromHex(sig_key, size * 2);
+    if (buf.size() == size) {
+        setEphemeralKeys(buf.data(), buf.data() + 32);
+    }
+#   else
+    m_rawSigKey = sig_key;
+#   endif
+}
+
+
 void xmrig::Job::copy(const Job &other)
 {
     m_algorithm  = other.m_algorithm;
@@ -164,6 +183,7 @@ void xmrig::Job::copy(const Job &other)
 
 #   ifdef XMRIG_PROXY_PROJECT
     m_rawSeedHash = other.m_rawSeedHash;
+    m_rawSigKey   = other.m_rawSigKey;
 
     memcpy(m_rawBlob, other.m_rawBlob, sizeof(m_rawBlob));
     memcpy(m_rawTarget, other.m_rawTarget, sizeof(m_rawTarget));
@@ -215,6 +235,7 @@ void xmrig::Job::move(Job &&other)
 
 #   ifdef XMRIG_PROXY_PROJECT
     m_rawSeedHash = std::move(other.m_rawSeedHash);
+    m_rawSigKey   = std::move(other.m_rawSigKey);
 
     memcpy(m_rawBlob, other.m_rawBlob, sizeof(m_rawBlob));
     memcpy(m_rawTarget, other.m_rawTarget, sizeof(m_rawTarget));
@@ -229,10 +250,11 @@ void xmrig::Job::move(Job &&other)
     memcpy(m_viewSecretKey, other.m_viewSecretKey, sizeof(m_viewSecretKey));
     memcpy(m_spendPublicKey, other.m_spendPublicKey, sizeof(m_spendPublicKey));
     memcpy(m_viewPublicKey, other.m_viewPublicKey, sizeof(m_viewPublicKey));
-    m_minerTxPrefix = std::move(other.m_minerTxPrefix);
-    m_minerTxEphPubKeyOffset = other.m_minerTxEphPubKeyOffset;
-    m_minerTxPubKeyOffset = other.m_minerTxPubKeyOffset;
-    m_minerTxMerkleTreeBranch = std::move(other.m_minerTxMerkleTreeBranch);
+
+    m_minerTxPrefix             = std::move(other.m_minerTxPrefix);
+    m_minerTxEphPubKeyOffset    = other.m_minerTxEphPubKeyOffset;
+    m_minerTxPubKeyOffset       = other.m_minerTxPubKeyOffset;
+    m_minerTxMerkleTreeBranch   = std::move(other.m_minerTxMerkleTreeBranch);
 #   else
     memcpy(m_ephPublicKey, other.m_ephPublicKey, sizeof(m_ephPublicKey));
     memcpy(m_ephSecretKey, other.m_ephSecretKey, sizeof(m_ephSecretKey));
@@ -245,26 +267,27 @@ void xmrig::Job::move(Job &&other)
 #ifdef XMRIG_PROXY_PROJECT
 
 
-void xmrig::Job::setSpendSecretKey(uint8_t* key)
+void xmrig::Job::setSpendSecretKey(const uint8_t *key)
 {
     m_hasMinerSignature = true;
     memcpy(m_spendSecretKey, key, sizeof(m_spendSecretKey));
-    xmrig::derive_view_secret_key(m_spendSecretKey, m_viewSecretKey);
-    xmrig::secret_key_to_public_key(m_spendSecretKey, m_spendPublicKey);
-    xmrig::secret_key_to_public_key(m_viewSecretKey, m_viewPublicKey);
+
+    derive_view_secret_key(m_spendSecretKey, m_viewSecretKey);
+    secret_key_to_public_key(m_spendSecretKey, m_spendPublicKey);
+    secret_key_to_public_key(m_viewSecretKey, m_viewPublicKey);
 }
 
 
-void xmrig::Job::setMinerTx(const uint8_t* begin, const uint8_t* end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, const Buffer& minerTxMerkleTreeBranch)
+void xmrig::Job::setMinerTx(const uint8_t *begin, const uint8_t *end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, const Buffer &minerTxMerkleTreeBranch)
 {
     m_minerTxPrefix.assign(begin, end);
-    m_minerTxEphPubKeyOffset = minerTxEphPubKeyOffset;
-    m_minerTxPubKeyOffset = minerTxPubKeyOffset;
-    m_minerTxMerkleTreeBranch = minerTxMerkleTreeBranch;
+    m_minerTxEphPubKeyOffset    = minerTxEphPubKeyOffset;
+    m_minerTxPubKeyOffset       = minerTxPubKeyOffset;
+    m_minerTxMerkleTreeBranch   = minerTxMerkleTreeBranch;
 }
 
 
-void xmrig::Job::generateHashingBlob(String& blob, String& signatureData) const
+void xmrig::Job::generateHashingBlob(String &signatureData)
 {
     uint8_t* eph_public_key = m_minerTxPrefix.data() + m_minerTxEphPubKeyOffset;
     uint8_t* txkey_pub = m_minerTxPrefix.data() + m_minerTxPubKeyOffset;
@@ -285,15 +308,14 @@ void xmrig::Job::generateHashingBlob(String& blob, String& signatureData) const
     generate_key_derivation(txkey_pub, m_viewSecretKey, derivation);
     derive_secret_key(derivation, 0, m_spendSecretKey, buf + 64);
 
-    signatureData = xmrig::Cvt::toHex(buf, sizeof(buf));
+    signatureData = Cvt::toHex(buf, sizeof(buf));
 
     uint8_t root_hash[32];
     const uint8_t* p = m_minerTxPrefix.data();
-    xmrig::BlockTemplate::CalculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash);
+    BlockTemplate::CalculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash);
 
-    blob = rawBlob();
     const uint64_t offset = nonceOffset() + nonceSize() + BlockTemplate::SIGNATURE_SIZE + 2 /* vote */;
-    xmrig::Cvt::toHex(blob.data() + offset * 2, 64, root_hash, 32);
+    Cvt::toHex(m_rawBlob + offset * 2, 64, root_hash, 32);
 }
 
 
diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h
index 85b01e789..42e6947f3 100644
--- a/src/base/net/stratum/Job.h
+++ b/src/base/net/stratum/Job.h
@@ -7,8 +7,8 @@
  * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
  * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
  * Copyright 2019      Howard Chu  <https://github.com/hyc>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2021 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
@@ -63,6 +63,7 @@ public:
     bool setSeedHash(const char *hash);
     bool setTarget(const char *target);
     void setDiff(uint64_t diff);
+    void setSigKey(const char *sig_key);
 
     inline bool isNicehash() const                      { return m_nicehash; }
     inline bool isValid() const                         { return (m_size > 0 && m_diff > 0) || !m_poolWallet.isEmpty(); }
@@ -102,6 +103,7 @@ public:
     inline const char *rawBlob() const                  { return m_rawBlob; }
     inline const char *rawTarget() const                { return m_rawTarget; }
     inline const String &rawSeedHash() const            { return m_rawSeedHash; }
+    inline const String &rawSigKey() const              { return m_rawSigKey; }
 #   endif
 
     static inline uint64_t toDiff(uint64_t target)      { return target ? (0xFFFFFFFFFFFFFFFFULL / target) : 0; }
@@ -117,13 +119,13 @@ public:
 #   endif
 
 #   ifdef XMRIG_PROXY_PROJECT
-    void setSpendSecretKey(uint8_t* key);
-    void setMinerTx(const uint8_t* begin, const uint8_t* end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, const Buffer& minerTxMerkleTreeBranch);
-    void generateHashingBlob(String& blob, String& signatureData) const;
+    void setSpendSecretKey(const uint8_t *key);
+    void setMinerTx(const uint8_t *begin, const uint8_t *end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, const Buffer &minerTxMerkleTreeBranch);
+    void generateHashingBlob(String &signatureData);
 #   else
     inline const uint8_t* ephSecretKey() const { return m_hasMinerSignature ? m_ephSecretKey : nullptr; }
 
-    inline void setEphemeralKeys(uint8_t* pub_key, uint8_t* sec_key)
+    inline void setEphemeralKeys(const uint8_t *pub_key, const uint8_t *sec_key)
     {
         m_hasMinerSignature = true;
         memcpy(m_ephPublicKey, pub_key, sizeof(m_ephSecretKey));
@@ -158,12 +160,13 @@ private:
     char m_rawBlob[kMaxBlobSize * 2 + 8]{};
     char m_rawTarget[24]{};
     String m_rawSeedHash;
+    String m_rawSigKey;
 
     // Miner signatures
-    uint8_t m_spendSecretKey[32];
-    uint8_t m_viewSecretKey[32];
-    uint8_t m_spendPublicKey[32];
-    uint8_t m_viewPublicKey[32];
+    uint8_t m_spendSecretKey[32]{};
+    uint8_t m_viewSecretKey[32]{};
+    uint8_t m_spendPublicKey[32]{};
+    uint8_t m_viewPublicKey[32]{};
     mutable Buffer m_minerTxPrefix;
     size_t m_minerTxEphPubKeyOffset = 0;
     size_t m_minerTxPubKeyOffset = 0;

From a30ede04f36a3b54d9fcdae654bf21d72222e37f Mon Sep 17 00:00:00 2001
From: SChernykh <sergey.v.chernykh@gmail.com>
Date: Mon, 5 Jul 2021 13:56:37 +0200
Subject: [PATCH 3/8] Updates from xmrig-proxy

---
 src/base/net/stratum/Client.cpp             | 15 ++--
 src/base/net/stratum/Client.h               |  4 +-
 src/base/net/stratum/DaemonClient.cpp       | 29 +++++---
 src/base/net/stratum/DaemonClient.h         |  1 +
 src/base/net/stratum/Job.cpp                | 81 ++++++++++++++++-----
 src/base/net/stratum/Job.h                  | 27 ++++---
 src/base/tools/cryptonote/BlobReader.h      |  2 +
 src/base/tools/cryptonote/BlockTemplate.cpp | 30 +++++++-
 src/base/tools/cryptonote/BlockTemplate.h   |  2 +
 9 files changed, 139 insertions(+), 52 deletions(-)

diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp
index 1871b6df0..95e1d8d05 100644
--- a/src/base/net/stratum/Client.cpp
+++ b/src/base/net/stratum/Client.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 2019      jtgrassie   <https://github.com/jtgrassie>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2021 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
@@ -225,6 +225,10 @@ int64_t xmrig::Client::submit(const JobResult &result)
     if (result.minerSignature()) {
         params.AddMember("sig", StringRef(signature), allocator);
     }
+#   else
+    if (result.sig) {
+        params.AddMember("sig", StringRef(result.sig), allocator);
+    }
 #   endif
 
     if (has<EXT_ALGO>() && result.algorithm.isValid()) {
@@ -440,12 +444,7 @@ bool xmrig::Client::parseJob(const rapidjson::Value &params, int *code)
         return false;
     }
 
-#   ifndef XMRIG_PROXY_PROJECT
-    uint8_t signatureKeyBuf[32 * 2];
-    if (Cvt::fromHex(signatureKeyBuf, sizeof(signatureKeyBuf), Json::getValue(params, "sig_key"))) {
-        job.setEphemeralKeys(signatureKeyBuf, signatureKeyBuf + 32);
-    }
-#   endif
+    job.setSigKey(Json::getString(params, "sig_key"));
 
     m_job.setClientId(m_rpcId);
 
diff --git a/src/base/net/stratum/Client.h b/src/base/net/stratum/Client.h
index fdaac9f15..d9164b8a3 100644
--- a/src/base/net/stratum/Client.h
+++ b/src/base/net/stratum/Client.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 2019      jtgrassie   <https://github.com/jtgrassie>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2021 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/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp
index 43493e752..c1d3d3a41 100644
--- a/src/base/net/stratum/DaemonClient.cpp
+++ b/src/base/net/stratum/DaemonClient.cpp
@@ -96,7 +96,7 @@ bool xmrig::DaemonClient::isTLS() const
 
 int64_t xmrig::DaemonClient::submit(const JobResult &result)
 {
-    if (result.jobId != (m_blocktemplateStr.data() + m_blocktemplateStr.size() - 32)) {
+    if (result.jobId != m_currentJobId) {
         return -1;
     }
 
@@ -114,6 +114,10 @@ int64_t xmrig::DaemonClient::submit(const JobResult &result)
         memcpy(data + m_blocktemplate.eph_public_key_index * 2, result.sig_data + 32 * 2, 32 * 2);
     }
 
+    if (result.extra_nonce >= 0) {
+        Cvt::toHex(data + m_blocktemplate.tx_extra_nonce_index * 2, 8, reinterpret_cast<const uint8_t*>(&result.extra_nonce), 4);
+    }
+
 #   else
 
     Cvt::toHex(data + m_job.nonceOffset() * 2, 8, reinterpret_cast<const uint8_t*>(&result.nonce), 4);
@@ -277,6 +281,19 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
         return false;
     }
 
+#   ifdef XMRIG_PROXY_PROJECT
+    const size_t k = m_blocktemplate.miner_tx_prefix_begin_index;
+    job.setMinerTx(
+        m_blocktemplate.raw_blob.data() + k,
+        m_blocktemplate.raw_blob.data() + m_blocktemplate.miner_tx_prefix_end_index,
+        m_blocktemplate.eph_public_key_index - k,
+        m_blocktemplate.tx_pubkey_index - k,
+        m_blocktemplate.tx_extra_nonce_index - k,
+        m_blocktemplate.tx_extra_nonce_size,
+        m_blocktemplate.miner_tx_merkle_tree_branch
+    );
+#   endif
+
     m_blockhashingblob = Json::getString(params, "blockhashing_blob");
 
     if (m_blocktemplate.has_miner_signature) {
@@ -308,13 +325,6 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
 
 #       ifdef XMRIG_PROXY_PROJECT
         job.setSpendSecretKey(secret_spendkey);
-        job.setMinerTx(
-            m_blocktemplate.raw_blob.data() + m_blocktemplate.miner_tx_prefix_begin_index,
-            m_blocktemplate.raw_blob.data() + m_blocktemplate.miner_tx_prefix_end_index,
-            m_blocktemplate.eph_public_key_index - m_blocktemplate.miner_tx_prefix_begin_index,
-            m_blocktemplate.tx_pubkey_index - m_blocktemplate.miner_tx_prefix_begin_index,
-            m_blocktemplate.miner_tx_merkle_tree_branch
-        );
 #       else
         uint8_t secret_viewkey[32];
         derive_view_secret_key(secret_spendkey, secret_viewkey);
@@ -377,7 +387,8 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
     job.setHeight(Json::getUint64(params, kHeight));
     job.setDiff(Json::getUint64(params, "difficulty"));
 
-    job.setId(blocktemplate.data() + blocktemplate.size() - 32);
+    m_currentJobId = Cvt::toHex(Cvt::randomBytes(4));
+    job.setId(m_currentJobId);
 
     m_job              = std::move(job);
     m_blocktemplateStr = std::move(blocktemplate);
diff --git a/src/base/net/stratum/DaemonClient.h b/src/base/net/stratum/DaemonClient.h
index 45a844646..f701240e5 100644
--- a/src/base/net/stratum/DaemonClient.h
+++ b/src/base/net/stratum/DaemonClient.h
@@ -84,6 +84,7 @@ private:
     } m_apiVersion = API_MONERO;
 
     std::shared_ptr<IHttpListener> m_httpListener;
+    String m_currentJobId;
     String m_blocktemplateStr;
     String m_blockhashingblob;
     String m_prevHash;
diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp
index c92f6f015..920bf95f7 100644
--- a/src/base/net/stratum/Job.cpp
+++ b/src/base/net/stratum/Job.cpp
@@ -7,8 +7,8 @@
  * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
  * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
  * Copyright 2019      Howard Chu  <https://github.com/hyc>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2021 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
@@ -144,6 +144,25 @@ void xmrig::Job::setDiff(uint64_t diff)
 }
 
 
+void xmrig::Job::setSigKey(const char *sig_key)
+{
+    constexpr const size_t size = 64;
+
+    if (!sig_key || strlen(sig_key) != size * 2) {
+        return;
+    }
+
+#   ifndef XMRIG_PROXY_PROJECT
+    const auto buf = Cvt::fromHex(sig_key, size * 2);
+    if (buf.size() == size) {
+        setEphemeralKeys(buf.data(), buf.data() + 32);
+    }
+#   else
+    m_rawSigKey = sig_key;
+#   endif
+}
+
+
 void xmrig::Job::copy(const Job &other)
 {
     m_algorithm  = other.m_algorithm;
@@ -164,6 +183,7 @@ void xmrig::Job::copy(const Job &other)
 
 #   ifdef XMRIG_PROXY_PROJECT
     m_rawSeedHash = other.m_rawSeedHash;
+    m_rawSigKey   = other.m_rawSigKey;
 
     memcpy(m_rawBlob, other.m_rawBlob, sizeof(m_rawBlob));
     memcpy(m_rawTarget, other.m_rawTarget, sizeof(m_rawTarget));
@@ -181,6 +201,8 @@ void xmrig::Job::copy(const Job &other)
     m_minerTxPrefix = other.m_minerTxPrefix;
     m_minerTxEphPubKeyOffset = other.m_minerTxEphPubKeyOffset;
     m_minerTxPubKeyOffset = other.m_minerTxPubKeyOffset;
+    m_minerTxExtraNonceOffset = other.m_minerTxExtraNonceOffset;
+    m_minerTxExtraNonceSize = other.m_minerTxExtraNonceSize;
     m_minerTxMerkleTreeBranch = other.m_minerTxMerkleTreeBranch;
 #   else
     memcpy(m_ephPublicKey, other.m_ephPublicKey, sizeof(m_ephPublicKey));
@@ -215,6 +237,7 @@ void xmrig::Job::move(Job &&other)
 
 #   ifdef XMRIG_PROXY_PROJECT
     m_rawSeedHash = std::move(other.m_rawSeedHash);
+    m_rawSigKey   = std::move(other.m_rawSigKey);
 
     memcpy(m_rawBlob, other.m_rawBlob, sizeof(m_rawBlob));
     memcpy(m_rawTarget, other.m_rawTarget, sizeof(m_rawTarget));
@@ -229,10 +252,13 @@ void xmrig::Job::move(Job &&other)
     memcpy(m_viewSecretKey, other.m_viewSecretKey, sizeof(m_viewSecretKey));
     memcpy(m_spendPublicKey, other.m_spendPublicKey, sizeof(m_spendPublicKey));
     memcpy(m_viewPublicKey, other.m_viewPublicKey, sizeof(m_viewPublicKey));
-    m_minerTxPrefix = std::move(other.m_minerTxPrefix);
-    m_minerTxEphPubKeyOffset = other.m_minerTxEphPubKeyOffset;
-    m_minerTxPubKeyOffset = other.m_minerTxPubKeyOffset;
-    m_minerTxMerkleTreeBranch = std::move(other.m_minerTxMerkleTreeBranch);
+
+    m_minerTxPrefix             = std::move(other.m_minerTxPrefix);
+    m_minerTxEphPubKeyOffset    = other.m_minerTxEphPubKeyOffset;
+    m_minerTxPubKeyOffset       = other.m_minerTxPubKeyOffset;
+    m_minerTxExtraNonceOffset   = other.m_minerTxExtraNonceOffset;
+    m_minerTxExtraNonceSize     = other.m_minerTxExtraNonceSize;
+    m_minerTxMerkleTreeBranch   = std::move(other.m_minerTxMerkleTreeBranch);
 #   else
     memcpy(m_ephPublicKey, other.m_ephPublicKey, sizeof(m_ephPublicKey));
     memcpy(m_ephSecretKey, other.m_ephSecretKey, sizeof(m_ephSecretKey));
@@ -245,26 +271,35 @@ void xmrig::Job::move(Job &&other)
 #ifdef XMRIG_PROXY_PROJECT
 
 
-void xmrig::Job::setSpendSecretKey(uint8_t* key)
+void xmrig::Job::setSpendSecretKey(const uint8_t *key)
 {
     m_hasMinerSignature = true;
     memcpy(m_spendSecretKey, key, sizeof(m_spendSecretKey));
-    xmrig::derive_view_secret_key(m_spendSecretKey, m_viewSecretKey);
-    xmrig::secret_key_to_public_key(m_spendSecretKey, m_spendPublicKey);
-    xmrig::secret_key_to_public_key(m_viewSecretKey, m_viewPublicKey);
+
+    derive_view_secret_key(m_spendSecretKey, m_viewSecretKey);
+    secret_key_to_public_key(m_spendSecretKey, m_spendPublicKey);
+    secret_key_to_public_key(m_viewSecretKey, m_viewPublicKey);
 }
 
 
-void xmrig::Job::setMinerTx(const uint8_t* begin, const uint8_t* end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, const Buffer& minerTxMerkleTreeBranch)
+void xmrig::Job::setMinerTx(const uint8_t *begin, const uint8_t *end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, size_t minerTxExtraNonceOffset, size_t minerTxExtraNonceSize, const Buffer &minerTxMerkleTreeBranch)
 {
     m_minerTxPrefix.assign(begin, end);
-    m_minerTxEphPubKeyOffset = minerTxEphPubKeyOffset;
-    m_minerTxPubKeyOffset = minerTxPubKeyOffset;
-    m_minerTxMerkleTreeBranch = minerTxMerkleTreeBranch;
+    m_minerTxEphPubKeyOffset    = minerTxEphPubKeyOffset;
+    m_minerTxPubKeyOffset       = minerTxPubKeyOffset;
+    m_minerTxExtraNonceOffset   = minerTxExtraNonceOffset;
+    m_minerTxExtraNonceSize     = minerTxExtraNonceSize;
+    m_minerTxMerkleTreeBranch   = minerTxMerkleTreeBranch;
 }
 
 
-void xmrig::Job::generateHashingBlob(String& blob, String& signatureData) const
+void xmrig::Job::setExtraNonceInMinerTx(uint32_t extra_nonce)
+{
+    memcpy(m_minerTxPrefix.data() + m_minerTxExtraNonceOffset, &extra_nonce, std::min(m_minerTxExtraNonceSize, sizeof(uint32_t)));
+}
+
+
+void xmrig::Job::generateSignatureData(String &signatureData) const
 {
     uint8_t* eph_public_key = m_minerTxPrefix.data() + m_minerTxEphPubKeyOffset;
     uint8_t* txkey_pub = m_minerTxPrefix.data() + m_minerTxPubKeyOffset;
@@ -285,15 +320,23 @@ void xmrig::Job::generateHashingBlob(String& blob, String& signatureData) const
     generate_key_derivation(txkey_pub, m_viewSecretKey, derivation);
     derive_secret_key(derivation, 0, m_spendSecretKey, buf + 64);
 
-    signatureData = xmrig::Cvt::toHex(buf, sizeof(buf));
+    signatureData = Cvt::toHex(buf, sizeof(buf));
+}
 
+void xmrig::Job::generateHashingBlob(String &blob) const
+{
     uint8_t root_hash[32];
     const uint8_t* p = m_minerTxPrefix.data();
-    xmrig::BlockTemplate::CalculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash);
+    BlockTemplate::CalculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash);
+
+    uint64_t root_hash_offset = nonceOffset() + nonceSize();
+
+    if (m_hasMinerSignature) {
+        root_hash_offset += BlockTemplate::SIGNATURE_SIZE + 2 /* vote */;
+    }
 
     blob = rawBlob();
-    const uint64_t offset = nonceOffset() + nonceSize() + BlockTemplate::SIGNATURE_SIZE + 2 /* vote */;
-    xmrig::Cvt::toHex(blob.data() + offset * 2, 64, root_hash, 32);
+    Cvt::toHex(blob.data() + root_hash_offset * 2, 64, root_hash, BlockTemplate::HASH_SIZE);
 }
 
 
diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h
index 85b01e789..c72902756 100644
--- a/src/base/net/stratum/Job.h
+++ b/src/base/net/stratum/Job.h
@@ -7,8 +7,8 @@
  * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
  * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
  * Copyright 2019      Howard Chu  <https://github.com/hyc>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2021 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
@@ -63,6 +63,7 @@ public:
     bool setSeedHash(const char *hash);
     bool setTarget(const char *target);
     void setDiff(uint64_t diff);
+    void setSigKey(const char *sig_key);
 
     inline bool isNicehash() const                      { return m_nicehash; }
     inline bool isValid() const                         { return (m_size > 0 && m_diff > 0) || !m_poolWallet.isEmpty(); }
@@ -102,6 +103,7 @@ public:
     inline const char *rawBlob() const                  { return m_rawBlob; }
     inline const char *rawTarget() const                { return m_rawTarget; }
     inline const String &rawSeedHash() const            { return m_rawSeedHash; }
+    inline const String &rawSigKey() const              { return m_rawSigKey; }
 #   endif
 
     static inline uint64_t toDiff(uint64_t target)      { return target ? (0xFFFFFFFFFFFFFFFFULL / target) : 0; }
@@ -117,13 +119,15 @@ public:
 #   endif
 
 #   ifdef XMRIG_PROXY_PROJECT
-    void setSpendSecretKey(uint8_t* key);
-    void setMinerTx(const uint8_t* begin, const uint8_t* end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, const Buffer& minerTxMerkleTreeBranch);
-    void generateHashingBlob(String& blob, String& signatureData) const;
+    void setSpendSecretKey(const uint8_t* key);
+    void setMinerTx(const uint8_t* begin, const uint8_t* end, size_t minerTxEphPubKeyOffset, size_t minerTxPubKeyOffset, size_t minerTxExtraNonceOffset, size_t minerTxExtraNonceSize, const Buffer& minerTxMerkleTreeBranch);
+    void setExtraNonceInMinerTx(uint32_t extra_nonce);
+    void generateSignatureData(String& signatureData) const;
+    void generateHashingBlob(String& blob) const;
 #   else
     inline const uint8_t* ephSecretKey() const { return m_hasMinerSignature ? m_ephSecretKey : nullptr; }
 
-    inline void setEphemeralKeys(uint8_t* pub_key, uint8_t* sec_key)
+    inline void setEphemeralKeys(const uint8_t *pub_key, const uint8_t *sec_key)
     {
         m_hasMinerSignature = true;
         memcpy(m_ephPublicKey, pub_key, sizeof(m_ephSecretKey));
@@ -158,15 +162,18 @@ private:
     char m_rawBlob[kMaxBlobSize * 2 + 8]{};
     char m_rawTarget[24]{};
     String m_rawSeedHash;
+    String m_rawSigKey;
 
     // Miner signatures
-    uint8_t m_spendSecretKey[32];
-    uint8_t m_viewSecretKey[32];
-    uint8_t m_spendPublicKey[32];
-    uint8_t m_viewPublicKey[32];
+    uint8_t m_spendSecretKey[32]{};
+    uint8_t m_viewSecretKey[32]{};
+    uint8_t m_spendPublicKey[32]{};
+    uint8_t m_viewPublicKey[32]{};
     mutable Buffer m_minerTxPrefix;
     size_t m_minerTxEphPubKeyOffset = 0;
     size_t m_minerTxPubKeyOffset = 0;
+    size_t m_minerTxExtraNonceOffset = 0;
+    size_t m_minerTxExtraNonceSize = 0;
     Buffer m_minerTxMerkleTreeBranch;
 #   else
     // Miner signatures
diff --git a/src/base/tools/cryptonote/BlobReader.h b/src/base/tools/cryptonote/BlobReader.h
index 58f81e2dd..f10bcd88b 100644
--- a/src/base/tools/cryptonote/BlobReader.h
+++ b/src/base/tools/cryptonote/BlobReader.h
@@ -61,6 +61,8 @@ public:
 
     inline size_t index() const { return m_index; }
 
+    inline void skip(size_t N) { m_index += N; }
+
 private:
     inline bool getByte(uint8_t& data)
     {
diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp
index 0b54c3998..5449ec23c 100644
--- a/src/base/tools/cryptonote/BlockTemplate.cpp
+++ b/src/base/tools/cryptonote/BlockTemplate.cpp
@@ -86,13 +86,35 @@ bool BlockTemplate::Init(const String& blockTemplate, Coin coin)
     ar(eph_public_key);
     ar(extra_size);
 
-    tx_pubkey_index = ar.index() + 1;
+    const uint64_t tx_extra_index = ar.index();
 
     ar.readItems(extra, extra_size);
 
-    // First thing in tx_extra must be TX_EXTRA_TAG_PUBKEY
-    if (extra[0] != 0x01)
-        return false;
+    CBlobReader ar_extra(extra.data(), extra_size);
+
+    tx_extra_nonce_size = 0;
+    tx_extra_nonce_index = 0;
+
+    while (ar_extra.index() < extra_size) {
+        uint64_t extra_tag;
+        ar_extra(extra_tag);
+
+        switch (extra_tag) {
+        case 0x01: // TX_EXTRA_TAG_PUBKEY
+            tx_pubkey_index = tx_extra_index + ar_extra.index();
+            ar_extra.skip(KEY_SIZE);
+            break;
+
+        case 0x02: // TX_EXTRA_NONCE
+            ar_extra(tx_extra_nonce_size);
+            tx_extra_nonce_index = tx_extra_index + ar_extra.index();
+            ar_extra.skip(tx_extra_nonce_size);
+            break;
+
+        default:
+            return false; // TODO: handle other tags
+        }
+    }
 
     miner_tx_prefix_end_index = ar.index();
     // Prefix end
diff --git a/src/base/tools/cryptonote/BlockTemplate.h b/src/base/tools/cryptonote/BlockTemplate.h
index e80ece141..4a6ef8051 100644
--- a/src/base/tools/cryptonote/BlockTemplate.h
+++ b/src/base/tools/cryptonote/BlockTemplate.h
@@ -42,6 +42,8 @@ struct BlockTemplate
     Buffer raw_blob;
     size_t eph_public_key_index;
     size_t tx_pubkey_index;
+    uint64_t tx_extra_nonce_size;
+    size_t tx_extra_nonce_index;
     size_t miner_tx_prefix_begin_index;
     size_t miner_tx_prefix_end_index;
 

From 93805cd167345e3bdd956263a495853b36184480 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Tue, 6 Jul 2021 23:07:31 +0700
Subject: [PATCH 4/8] #2476 Fixed crash in DMI memory reader.

---
 src/hw/dmi/DmiMemory.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/hw/dmi/DmiMemory.cpp b/src/hw/dmi/DmiMemory.cpp
index 883c89b5e..a07f5b550 100644
--- a/src/hw/dmi/DmiMemory.cpp
+++ b/src/hw/dmi/DmiMemory.cpp
@@ -230,6 +230,10 @@ void xmrig::DmiMemory::setId(const char *slot, const char *bank)
     m_slot = slot;
     m_bank = bank;
 
+    if (!slot || !bank) {
+        return;
+    }
+
     try {
         std::cmatch cm;
         if (std::regex_match(slot, cm, std::regex("^Channel([A-Z])[-_]DIMM(\\d+)$", std::regex_constants::icase))) {

From 0842e6b9d26422b8abd35e3a918128ff946aed4d Mon Sep 17 00:00:00 2001
From: SChernykh <sergey.v.chernykh@gmail.com>
Date: Thu, 15 Jul 2021 11:13:14 +0200
Subject: [PATCH 5/8] ZeroMQ support for solo mining

Gets new blocks from daemon immediately without polling, saving ~0.5 seconds on average when daemon gets new block from the network. Also saves some CPU cycles because it doesn't need to poll daemon every second.

Testing: add "daemon-zmq-port": 28083 to xmrig's pool config in config.json and run ./monerod --testnet --zmq-pub tcp://127.0.0.1:28083
---
 src/base/base.cmake                         |   1 +
 src/base/kernel/config/BaseTransform.cpp    |  18 +-
 src/base/kernel/interfaces/IConfig.h        |   1 +
 src/base/net/stratum/DaemonClient.cpp       | 369 +++++++++++++++++++-
 src/base/net/stratum/DaemonClient.h         |  45 ++-
 src/base/net/stratum/Pool.cpp               |   6 +
 src/base/net/stratum/Pool.h                 |   3 +
 src/base/tools/bswap_64.h                   |  37 ++
 src/base/tools/cryptonote/BlockTemplate.cpp |   2 +-
 src/core/config/Config_platform.h           |   1 +
 src/crypto/astrobwt/AstroBWT.cpp            |  16 +-
 src/net/Network.cpp                         |  18 +-
 12 files changed, 482 insertions(+), 35 deletions(-)
 create mode 100644 src/base/tools/bswap_64.h

diff --git a/src/base/base.cmake b/src/base/base.cmake
index 92c16c0d7..1359dd8a0 100644
--- a/src/base/base.cmake
+++ b/src/base/base.cmake
@@ -70,6 +70,7 @@ set(HEADERS_BASE
     src/base/net/tools/Storage.h
     src/base/tools/Arguments.h
     src/base/tools/Baton.h
+    src/base/tools/bswap_64.h
     src/base/tools/Buffer.h
     src/base/tools/Chrono.h
     src/base/tools/Cvt.h
diff --git a/src/base/kernel/config/BaseTransform.cpp b/src/base/kernel/config/BaseTransform.cpp
index 1036ae295..6d4f38d01 100644
--- a/src/base/kernel/config/BaseTransform.cpp
+++ b/src/base/kernel/config/BaseTransform.cpp
@@ -242,13 +242,14 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch
         return set(doc, BaseConfig::kTls, TlsConfig::kGen, arg);
 #   endif
 
-    case IConfig::RetriesKey:     /* --retries */
-    case IConfig::RetryPauseKey:  /* --retry-pause */
-    case IConfig::PrintTimeKey:   /* --print-time */
-    case IConfig::HttpPort:       /* --http-port */
-    case IConfig::DonateLevelKey: /* --donate-level */
-    case IConfig::DaemonPollKey:  /* --daemon-poll-interval */
-    case IConfig::DnsTtlKey:      /* --dns-ttl */
+    case IConfig::RetriesKey:       /* --retries */
+    case IConfig::RetryPauseKey:    /* --retry-pause */
+    case IConfig::PrintTimeKey:     /* --print-time */
+    case IConfig::HttpPort:         /* --http-port */
+    case IConfig::DonateLevelKey:   /* --donate-level */
+    case IConfig::DaemonPollKey:    /* --daemon-poll-interval */
+    case IConfig::DnsTtlKey:        /* --dns-ttl */
+    case IConfig::DaemonZMQPortKey: /* --daemon-zmq-port */
         return transformUint64(doc, key, static_cast<uint64_t>(strtol(arg, nullptr, 10)));
 
     case IConfig::BackgroundKey:  /* --background */
@@ -359,6 +360,9 @@ void xmrig::BaseTransform::transformUint64(rapidjson::Document &doc, int key, ui
 #   ifdef XMRIG_FEATURE_HTTP
     case IConfig::DaemonPollKey:  /* --daemon-poll-interval */
         return add(doc, Pools::kPools, Pool::kDaemonPollInterval, arg);
+
+    case IConfig::DaemonZMQPortKey:  /* --daemon-zmq-port */
+        return add(doc, Pools::kPools, Pool::kDaemonZMQPort, arg);
 #   endif
 
     default:
diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h
index d1260afa8..e531353aa 100644
--- a/src/base/kernel/interfaces/IConfig.h
+++ b/src/base/kernel/interfaces/IConfig.h
@@ -85,6 +85,7 @@ public:
         DnsIPv6Key           = 1053,
         DnsTtlKey            = 1054,
         SpendSecretKey       = 1055,
+        DaemonZMQPortKey     = 1056,
 
         // xmrig common
         CPUPriorityKey       = 1021,
diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp
index c1d3d3a41..77818f34c 100644
--- a/src/base/net/stratum/DaemonClient.cpp
+++ b/src/base/net/stratum/DaemonClient.cpp
@@ -31,10 +31,14 @@
 #include "base/io/json/JsonRequest.h"
 #include "base/io/log/Log.h"
 #include "base/kernel/interfaces/IClientListener.h"
+#include "base/net/dns/Dns.h"
+#include "base/net/dns/DnsRecords.h"
 #include "base/net/http/Fetch.h"
 #include "base/net/http/HttpData.h"
 #include "base/net/http/HttpListener.h"
 #include "base/net/stratum/SubmitResult.h"
+#include "base/net/tools/NetBuffer.h"
+#include "base/tools/bswap_64.h"
 #include "base/tools/Cvt.h"
 #include "base/tools/Timer.h"
 #include "base/tools/cryptonote/Signatures.h"
@@ -48,7 +52,11 @@
 
 namespace xmrig {
 
-static const char *kBlocktemplateBlob       = "blocktemplate_blob";
+
+Storage<DaemonClient> DaemonClient::m_storage;
+
+
+static const char* kBlocktemplateBlob = "blocktemplate_blob";
 static const char *kGetHeight               = "/getheight";
 static const char *kGetInfo                 = "/getinfo";
 static const char *kHash                    = "hash";
@@ -57,6 +65,12 @@ static const char *kJsonRPC                 = "/json_rpc";
 
 static constexpr size_t kBlobReserveSize    = 8;
 
+static const char kZMQGreeting[64] = { -1, 0, 0, 0, 0, 0, 0, 0, 0, 127, 3, 0, 'N', 'U', 'L', 'L' };
+static constexpr size_t kZMQGreetingSize1 = 11;
+
+static const char kZMQHandshake[] = "\4\x19\5READY\xbSocket-Type\0\0\0\3SUB";
+static const char kZMQSubscribe[] = "\0\x18\1json-minimal-chain_main";
+
 }
 
 
@@ -65,12 +79,25 @@ xmrig::DaemonClient::DaemonClient(int id, IClientListener *listener) :
 {
     m_httpListener  = std::make_shared<HttpListener>(this);
     m_timer         = new Timer(this);
+    m_key           = m_storage.add(this);
 }
 
 
 xmrig::DaemonClient::~DaemonClient()
 {
     delete m_timer;
+    delete m_ZMQSocket;
+}
+
+
+void xmrig::DaemonClient::deleteLater()
+{
+    if (m_pool.zmq_port() >= 0) {
+        ZMQClose(true);
+    }
+    else {
+        delete this;
+    }
 }
 
 
@@ -159,7 +186,13 @@ void xmrig::DaemonClient::connect()
     }
 
     setState(ConnectingState);
-    getBlockTemplate();
+
+    if (m_pool.zmq_port() >= 0) {
+        m_dns = Dns::resolve(m_pool.host(), this);
+    }
+    else {
+        getBlockTemplate();
+    }
 }
 
 
@@ -238,7 +271,7 @@ void xmrig::DaemonClient::onHttpData(const HttpData &data)
 void xmrig::DaemonClient::onTimer(const Timer *)
 {
     if (m_state == ConnectingState) {
-        getBlockTemplate();
+        connect();
     }
     else if (m_state == ConnectedState) {
         if (m_apiVersion == API_DERO) {
@@ -251,6 +284,43 @@ void xmrig::DaemonClient::onTimer(const Timer *)
 }
 
 
+void xmrig::DaemonClient::onResolved(const DnsRecords& records, int status, const char* error)
+{
+    m_dns.reset();
+
+    if (status < 0 && records.isEmpty()) {
+        if (!isQuiet()) {
+            LOG_ERR("%s " RED("DNS error: ") RED_BOLD("\"%s\""), tag(), error);
+        }
+
+        retry();
+        return;
+    }
+
+    if (m_ZMQSocket) {
+        delete m_ZMQSocket;
+    }
+
+    const auto& record = records.get();
+    m_ip = record.ip();
+
+    uv_connect_t* req = new uv_connect_t;
+    req->data = m_storage.ptr(m_key);
+
+    m_ZMQSocket = new uv_tcp_t;
+    m_ZMQSocket->data = m_storage.ptr(m_key);
+
+    uv_tcp_init(uv_default_loop(), m_ZMQSocket);
+    uv_tcp_nodelay(m_ZMQSocket, 1);
+
+#   ifndef WIN32
+    uv_tcp_keepalive(m_ZMQSocket, 1, 60);
+#   endif
+
+    uv_tcp_connect(req, m_ZMQSocket, record.addr(m_pool.zmq_port()), onZMQConnect);
+}
+
+
 bool xmrig::DaemonClient::isOutdated(uint64_t height, const char *hash) const
 {
     return m_job.height() != height || m_prevHash != hash;
@@ -452,7 +522,9 @@ bool xmrig::DaemonClient::parseResponse(int64_t id, const rapidjson::Value &resu
     }
 
     if (handleSubmitResponse(id, error_msg)) {
-        getBlockTemplate();
+        if (error_msg || (m_pool.zmq_port() < 0)) {
+            getBlockTemplate();
+        }
         return true;
     }
 
@@ -504,6 +576,10 @@ void xmrig::DaemonClient::retry()
         setState(ConnectingState);
     }
 
+    if ((m_ZMQConnectionState != ZMQ_NOT_CONNECTED) && (m_ZMQConnectionState != ZMQ_DISCONNECTING)) {
+        uv_close(reinterpret_cast<uv_handle_t*>(m_ZMQSocket), onZMQClose);
+    }
+
     m_timer->stop();
     m_timer->start(m_retryPause, 0);
 }
@@ -531,8 +607,10 @@ void xmrig::DaemonClient::setState(SocketState state)
             m_failures = 0;
             m_listener->onLoginSuccess(this);
 
-            const uint64_t interval = std::max<uint64_t>(20, m_pool.pollInterval());
-            m_timer->start(interval, interval);
+            if (m_pool.zmq_port() < 0) {
+                const uint64_t interval = std::max<uint64_t>(20, m_pool.pollInterval());
+                m_timer->start(interval, interval);
+            }
         }
         break;
 
@@ -545,3 +623,282 @@ void xmrig::DaemonClient::setState(SocketState state)
         break;
     }
 }
+
+
+void xmrig::DaemonClient::onZMQConnect(uv_connect_t* req, int status)
+{
+    DaemonClient* client = getClient(req->data);
+    delete req;
+
+    if (!client) {
+        return;
+    }
+
+    if (status < 0) {
+        LOG_ERR("%s " RED("ZMQ connect error: ") RED_BOLD("\"%s\""), client->tag(), uv_strerror(status));
+        client->retry();
+        return;
+    }
+
+    client->ZMQConnected();
+}
+
+
+void xmrig::DaemonClient::onZMQRead(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
+{
+    DaemonClient* client = getClient(stream->data);
+    if (client) {
+        client->ZMQRead(nread, buf);
+    }
+
+    NetBuffer::release(buf);
+}
+
+
+void xmrig::DaemonClient::onZMQClose(uv_handle_t* handle)
+{
+    DaemonClient* client = getClient(handle->data);
+    if (client) {
+#       ifdef APP_DEBUG
+        LOG_DEBUG(CYAN("tcp-zmq://%s:%u") BLACK_BOLD(" disconnected"), client->m_pool.host().data(), client->m_pool.zmq_port());
+#       endif
+        client->m_ZMQConnectionState = ZMQ_NOT_CONNECTED;
+    }
+}
+
+
+void xmrig::DaemonClient::onZMQShutdown(uv_handle_t* handle)
+{
+    DaemonClient* client = getClient(handle->data);
+    if (client) {
+#       ifdef APP_DEBUG
+        LOG_DEBUG(CYAN("tcp-zmq://%s:%u") BLACK_BOLD(" shutdown"), client->m_pool.host().data(), client->m_pool.zmq_port());
+#       endif
+        client->m_ZMQConnectionState = ZMQ_NOT_CONNECTED;
+        m_storage.remove(client->m_key);
+    }
+}
+
+
+void xmrig::DaemonClient::ZMQConnected()
+{
+#   ifdef APP_DEBUG
+    LOG_DEBUG(CYAN("tcp-zmq://%s:%u") BLACK_BOLD(" connected"), m_pool.host().data(), m_pool.zmq_port());
+#   endif
+
+    m_ZMQConnectionState = ZMQ_GREETING_1;
+    m_ZMQSendBuf.reserve(256);
+    m_ZMQRecvBuf.reserve(256);
+
+    if (ZMQWrite(kZMQGreeting, kZMQGreetingSize1)) {
+        uv_read_start(reinterpret_cast<uv_stream_t*>(m_ZMQSocket), NetBuffer::onAlloc, onZMQRead);
+    }
+}
+
+
+bool xmrig::DaemonClient::ZMQWrite(const char* data, size_t size)
+{
+    m_ZMQSendBuf.assign(data, data + size);
+
+    uv_buf_t buf;
+    buf.base = m_ZMQSendBuf.data();
+    buf.len = static_cast<uint32_t>(m_ZMQSendBuf.size());
+
+    const int rc = uv_try_write(reinterpret_cast<uv_stream_t*>(m_ZMQSocket), &buf, 1);
+
+    if (static_cast<size_t>(rc) == buf.len) {
+        return true;
+    }
+
+    LOG_ERR("%s " RED("ZMQ write failed, rc = %d"), tag(), rc);
+    ZMQClose();
+    return false;
+}
+
+
+void xmrig::DaemonClient::ZMQRead(ssize_t nread, const uv_buf_t* buf)
+{
+    if (nread <= 0) {
+        LOG_ERR("%s " RED("ZMQ read failed, nread = %" PRId64), tag(), nread);
+        ZMQClose();
+        return;
+    }
+
+    m_ZMQRecvBuf.insert(m_ZMQRecvBuf.end(), buf->base, buf->base + nread);
+
+    do {
+        switch (m_ZMQConnectionState) {
+        case ZMQ_GREETING_1:
+            if (m_ZMQRecvBuf.size() >= kZMQGreetingSize1) {
+                if ((m_ZMQRecvBuf[0] == -1) && (m_ZMQRecvBuf[9] == 127) && (m_ZMQRecvBuf[10] == 3)) {
+                    ZMQWrite(kZMQGreeting + kZMQGreetingSize1, sizeof(kZMQGreeting) - kZMQGreetingSize1);
+                    m_ZMQConnectionState = ZMQ_GREETING_2;
+                    break;
+                }
+                else {
+                    LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format"), tag());
+                    ZMQClose();
+                }
+            }
+            return;
+
+        case ZMQ_GREETING_2:
+            if (m_ZMQRecvBuf.size() >= sizeof(kZMQGreeting)) {
+                if (memcmp(m_ZMQRecvBuf.data() + 12, kZMQGreeting + 12, 20) == 0) {
+                    m_ZMQConnectionState = ZMQ_HANDSHAKE;
+                    m_ZMQRecvBuf.erase(m_ZMQRecvBuf.begin(), m_ZMQRecvBuf.begin() + sizeof(kZMQGreeting));
+
+                    ZMQWrite(kZMQHandshake, sizeof(kZMQHandshake) - 1);
+                    break;
+                }
+                else {
+                    LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format 2"), tag());
+                    ZMQClose();
+                }
+            }
+            return;
+
+        case ZMQ_HANDSHAKE:
+            if (m_ZMQRecvBuf.size() >= 2) {
+                if (m_ZMQRecvBuf[0] != 4) {
+                    LOG_ERR("%s " RED("ZMQ handshake failed: invalid handshake format"), tag());
+                    ZMQClose();
+                    return;
+                }
+
+                const size_t size = static_cast<unsigned char>(m_ZMQRecvBuf[1]);
+                if (size < 18) {
+                    LOG_ERR("%s " RED("ZMQ handshake failed: invalid handshake size"), tag());
+                    ZMQClose();
+                    return;
+                }
+
+                if (m_ZMQRecvBuf.size() < size + 2) {
+                    return;
+                }
+
+                if (memcmp(m_ZMQRecvBuf.data() + 2, kZMQHandshake + 2, 18) != 0) {
+                    LOG_ERR("%s " RED("ZMQ handshake failed: invalid handshake data"), tag());
+                    ZMQClose();
+                    return;
+                }
+
+                ZMQWrite(kZMQSubscribe, sizeof(kZMQSubscribe) - 1);
+
+                m_ZMQConnectionState = ZMQ_CONNECTED;
+                m_ZMQRecvBuf.erase(m_ZMQRecvBuf.begin(), m_ZMQRecvBuf.begin() + size + 2);
+
+                getBlockTemplate();
+                break;
+            }
+            return;
+
+        case ZMQ_CONNECTED:
+            ZMQParse();
+            return;
+
+        default:
+            return;
+        }
+    } while (true);
+}
+
+
+void xmrig::DaemonClient::ZMQParse()
+{
+#   ifdef APP_DEBUG
+    std::vector<char> msg;
+#   endif
+
+    size_t msg_size = 0;
+
+    char* data = m_ZMQRecvBuf.data();
+    size_t avail = m_ZMQRecvBuf.size();
+    bool more;
+
+    do {
+        if (avail < 1) {
+            return;
+        }
+
+        more                 = (data[0] & 1) != 0;
+        const bool long_size = (data[0] & 2) != 0;
+        const bool command   = (data[0] & 4) != 0;
+
+        ++data;
+        --avail;
+
+        uint64_t size = 0;
+        if (long_size)
+        {
+            if (avail < sizeof(uint64_t)) {
+                return;
+            }
+            size = bswap_64(*((uint64_t*)data));
+            data += sizeof(uint64_t);
+            avail -= sizeof(uint64_t);
+        }
+        else
+        {
+            if (avail < sizeof(uint8_t)) {
+                return;
+            }
+            size = static_cast<uint8_t>(*data);
+            ++data;
+            --avail;
+        }
+
+        if (size > 1024U - msg_size)
+        {
+            LOG_ERR("%s " RED("ZMQ message is too large, size = %" PRIu64 " bytes"), tag(), size);
+            ZMQClose();
+            return;
+        }
+
+        if (avail < size) {
+            return;
+        }
+
+        if (!command) {
+#           ifdef APP_DEBUG
+            msg.insert(msg.end(), data, data + size);
+#           endif
+
+            msg_size += size;
+        }
+
+        data += size;
+        avail -= size;
+    } while (more);
+
+    m_ZMQRecvBuf.erase(m_ZMQRecvBuf.begin(), m_ZMQRecvBuf.begin() + (data - m_ZMQRecvBuf.data()));
+
+#   ifdef APP_DEBUG
+    LOG_DEBUG(CYAN("tcp-zmq://%s:%u") BLACK_BOLD(" read ") CYAN_BOLD("%zu") BLACK_BOLD(" bytes") " %s", m_pool.host().data(), m_pool.zmq_port(), msg.size(), msg.data());
+#   endif
+
+    getBlockTemplate();
+}
+
+
+bool xmrig::DaemonClient::ZMQClose(bool shutdown)
+{
+    if ((m_ZMQConnectionState == ZMQ_NOT_CONNECTED) || (m_ZMQConnectionState == ZMQ_DISCONNECTING)) {
+        if (shutdown) {
+            m_storage.remove(m_key);
+        }
+        return false;
+    }
+
+    m_ZMQConnectionState = ZMQ_DISCONNECTING;
+
+    if (uv_is_closing(reinterpret_cast<uv_handle_t*>(m_ZMQSocket)) == 0) {
+        uv_close(reinterpret_cast<uv_handle_t*>(m_ZMQSocket), shutdown ? onZMQShutdown : onZMQClose);
+        if (!shutdown) {
+            retry();
+        }
+        return true;
+    }
+
+    return false;
+}
diff --git a/src/base/net/stratum/DaemonClient.h b/src/base/net/stratum/DaemonClient.h
index f701240e5..2844d15fe 100644
--- a/src/base/net/stratum/DaemonClient.h
+++ b/src/base/net/stratum/DaemonClient.h
@@ -27,11 +27,16 @@
 #define XMRIG_DAEMONCLIENT_H
 
 
+#include <uv.h>
+
+
+#include "base/kernel/interfaces/IDnsListener.h"
 #include "base/kernel/interfaces/IHttpListener.h"
 #include "base/kernel/interfaces/ITimerListener.h"
 #include "base/net/stratum/BaseClient.h"
 #include "base/tools/Object.h"
 #include "base/tools/cryptonote/BlockTemplate.h"
+#include "base/net/tools/Storage.h"
 
 
 #include <memory>
@@ -40,7 +45,10 @@
 namespace xmrig {
 
 
-class DaemonClient : public BaseClient, public ITimerListener, public IHttpListener
+class DnsRequest;
+
+
+class DaemonClient : public BaseClient, public IDnsListener, public ITimerListener, public IHttpListener
 {
 public:
     XMRIG_DISABLE_COPY_MOVE_DEFAULT(DaemonClient)
@@ -57,6 +65,7 @@ protected:
 
     void onHttpData(const HttpData &data) override;
     void onTimer(const Timer *timer) override;
+    void onResolved(const DnsRecords& records, int status, const char* error) override;
 
     inline bool hasExtension(Extension) const noexcept override         { return false; }
     inline const char *mode() const override                            { return "daemon"; }
@@ -64,7 +73,7 @@ protected:
     inline const char *tlsVersion() const override                      { return m_tlsVersion; }
     inline int64_t send(const rapidjson::Value &, Callback) override    { return -1; }
     inline int64_t send(const rapidjson::Value &) override              { return -1; }
-    inline void deleteLater() override                                  { delete this; }
+    void deleteLater() override;
     inline void tick(uint64_t) override                                 {}
 
 private:
@@ -95,6 +104,38 @@ private:
     String m_blocktemplateRequestHash;
 
     BlockTemplate m_blocktemplate;
+
+private:
+    static inline DaemonClient* getClient(void* data) { return m_storage.get(data); }
+
+    uintptr_t m_key = 0;
+    static Storage<DaemonClient> m_storage;
+
+    static void onZMQConnect(uv_connect_t* req, int status);
+    static void onZMQRead(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
+    static void onZMQClose(uv_handle_t* handle);
+    static void onZMQShutdown(uv_handle_t* handle);
+
+    void ZMQConnected();
+    bool ZMQWrite(const char* data, size_t size);
+    void ZMQRead(ssize_t nread, const uv_buf_t* buf);
+    void ZMQParse();
+    bool ZMQClose(bool shutdown = false);
+
+    std::shared_ptr<DnsRequest> m_dns;
+    uv_tcp_t* m_ZMQSocket = nullptr;
+
+    enum {
+        ZMQ_NOT_CONNECTED,
+        ZMQ_GREETING_1,
+        ZMQ_GREETING_2,
+        ZMQ_HANDSHAKE,
+        ZMQ_CONNECTED,
+        ZMQ_DISCONNECTING,
+    } m_ZMQConnectionState = ZMQ_NOT_CONNECTED;
+
+    std::vector<char> m_ZMQSendBuf;
+    std::vector<char> m_ZMQRecvBuf;
 };
 
 
diff --git a/src/base/net/stratum/Pool.cpp b/src/base/net/stratum/Pool.cpp
index 86e3205e2..427a7e8fc 100644
--- a/src/base/net/stratum/Pool.cpp
+++ b/src/base/net/stratum/Pool.cpp
@@ -66,6 +66,7 @@ const char *Pool::kAlgo                   = "algo";
 const char *Pool::kCoin                   = "coin";
 const char *Pool::kDaemon                 = "daemon";
 const char *Pool::kDaemonPollInterval     = "daemon-poll-interval";
+const char *Pool::kDaemonZMQPort          = "daemon-zmq-port";
 const char *Pool::kEnabled                = "enabled";
 const char *Pool::kFingerprint            = "tls-fingerprint";
 const char *Pool::kKeepalive              = "keepalive";
@@ -127,6 +128,7 @@ xmrig::Pool::Pool(const rapidjson::Value &object) :
     m_coin           = Json::getString(object, kCoin);
     m_daemon         = Json::getString(object, kSelfSelect);
     m_proxy          = Json::getValue(object, kSOCKS5);
+    m_zmqPort        = Json::getInt(object, kDaemonZMQPort, m_zmqPort);
 
     m_flags.set(FLAG_ENABLED,  Json::getBool(object, kEnabled, true));
     m_flags.set(FLAG_NICEHASH, Json::getBool(object, kNicehash) || m_url.host().contains(kNicehashHost));
@@ -301,6 +303,7 @@ rapidjson::Value xmrig::Pool::toJSON(rapidjson::Document &doc) const
 
     if (m_mode == MODE_DAEMON) {
         obj.AddMember(StringRef(kDaemonPollInterval), m_pollInterval, allocator);
+        obj.AddMember(StringRef(kDaemonZMQPort), m_zmqPort, allocator);
     }
     else {
         obj.AddMember(StringRef(kSelfSelect),     m_daemon.url().toJSON(), allocator);
@@ -336,6 +339,9 @@ void xmrig::Pool::print() const
     LOG_NOTICE("url:       %s", url().data());
     LOG_DEBUG ("host:      %s", host().data());
     LOG_DEBUG ("port:      %d", static_cast<int>(port()));
+    if (m_zmqPort >= 0) {
+        LOG_DEBUG("zmq-port:  %d", m_zmqPort);
+    }
     LOG_DEBUG ("user:      %s", m_user.data());
     LOG_DEBUG ("pass:      %s", m_password.data());
     LOG_DEBUG ("rig-id     %s", m_rigId.data());
diff --git a/src/base/net/stratum/Pool.h b/src/base/net/stratum/Pool.h
index c7abbbf38..4eb00ec01 100644
--- a/src/base/net/stratum/Pool.h
+++ b/src/base/net/stratum/Pool.h
@@ -72,6 +72,7 @@ public:
     static const char *kUrl;
     static const char *kUser;
     static const char* kSpendSecretKey;
+    static const char* kDaemonZMQPort;
     static const char *kNicehashHost;
 
     constexpr static int kKeepAliveTimeout         = 60;
@@ -107,6 +108,7 @@ public:
     inline int keepAlive() const                        { return m_keepAlive; }
     inline Mode mode() const                            { return m_mode; }
     inline uint16_t port() const                        { return m_url.port(); }
+    inline int zmq_port() const                         { return m_zmqPort; }
     inline uint64_t pollInterval() const                { return m_pollInterval; }
     inline void setAlgo(const Algorithm &algorithm)     { m_algorithm = algorithm; }
     inline void setPassword(const String &password)     { m_password = password; }
@@ -155,6 +157,7 @@ private:
     uint64_t m_pollInterval         = kDefaultPollInterval;
     Url m_daemon;
     Url m_url;
+    int m_zmqPort                   = -1;
 
 #   ifdef XMRIG_FEATURE_BENCHMARK
     std::shared_ptr<BenchConfig> m_benchmark;
diff --git a/src/base/tools/bswap_64.h b/src/base/tools/bswap_64.h
new file mode 100644
index 000000000..96335c64e
--- /dev/null
+++ b/src/base/tools/bswap_64.h
@@ -0,0 +1,37 @@
+/* XMRig
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 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_BSWAP_64_H
+#define XMRIG_BSWAP_64_H
+
+#ifdef _MSC_VER
+
+#include <stdlib.h>
+#define bswap_64(x) _byteswap_uint64(x)
+
+#elif defined __GNUC__
+
+#define bswap_64(x) __builtin_bswap64(x)
+
+#else
+
+#include <byteswap.h>
+
+#endif
+
+#endif /* XMRIG_BSWAP_64_H */
diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp
index 5449ec23c..29653441d 100644
--- a/src/base/tools/cryptonote/BlockTemplate.cpp
+++ b/src/base/tools/cryptonote/BlockTemplate.cpp
@@ -96,7 +96,7 @@ bool BlockTemplate::Init(const String& blockTemplate, Coin coin)
     tx_extra_nonce_index = 0;
 
     while (ar_extra.index() < extra_size) {
-        uint64_t extra_tag;
+        uint64_t extra_tag = 0;
         ar_extra(extra_tag);
 
         switch (extra_tag) {
diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h
index 2867a8b7a..7194f5cc5 100644
--- a/src/core/config/Config_platform.h
+++ b/src/core/config/Config_platform.h
@@ -52,6 +52,7 @@ static const option options[] = {
     { "daemon-poll-interval",  1, nullptr, IConfig::DaemonPollKey         },
     { "self-select",           1, nullptr, IConfig::SelfSelectKey         },
     { "submit-to-origin",      0, nullptr, IConfig::SubmitToOriginKey     },
+    { "daemon-zmq-port",       1, nullptr, IConfig::DaemonZMQPortKey      },
 #   endif
     { "av",                    1, nullptr, IConfig::AVKey                 },
     { "background",            0, nullptr, IConfig::BackgroundKey         },
diff --git a/src/crypto/astrobwt/AstroBWT.cpp b/src/crypto/astrobwt/AstroBWT.cpp
index 2286a5fe2..3e3622530 100644
--- a/src/crypto/astrobwt/AstroBWT.cpp
+++ b/src/crypto/astrobwt/AstroBWT.cpp
@@ -30,6 +30,7 @@
 #include "crypto/astrobwt/AstroBWT.h"
 #include "backend/cpu/Cpu.h"
 #include "base/crypto/sha3.h"
+#include "base/tools/bswap_64.h"
 #include "crypto/cn/CryptoNight.h"
 
 
@@ -54,21 +55,6 @@ __attribute__((ms_abi))
 void SHA3_256_AVX2_ASM(const void* in, size_t inBytes, void* out);
 #endif
 
-#ifdef _MSC_VER
-
-#include <stdlib.h>
-#define bswap_64(x) _byteswap_uint64(x)
-
-#elif defined __GNUC__
-
-#define bswap_64(x) __builtin_bswap64(x)
-
-#else
-
-#include <byteswap.h>
-
-#endif
-
 #ifdef XMRIG_ARM
 extern "C" {
 #include "salsa20_ref/ecrypt-sync.h"
diff --git a/src/net/Network.cpp b/src/net/Network.cpp
index fb8b21b3d..64a1b689e 100644
--- a/src/net/Network.cpp
+++ b/src/net/Network.cpp
@@ -137,9 +137,14 @@ void xmrig::Network::onActive(IStrategy *strategy, IClient *client)
     }
 #   endif
 
+    char zmq_buf[32] = {};
+    if (client->pool().zmq_port() >= 0) {
+        snprintf(zmq_buf, sizeof(zmq_buf), " (ZMQ:%d)", client->pool().zmq_port());
+    }
+
     const char *tlsVersion = client->tlsVersion();
-    LOG_INFO("%s " WHITE_BOLD("use %s ") CYAN_BOLD("%s:%d ") GREEN_BOLD("%s") " " BLACK_BOLD("%s"),
-             Tags::network(), client->mode(), pool.host().data(), pool.port(), tlsVersion ? tlsVersion : "", client->ip().data());
+    LOG_INFO("%s " WHITE_BOLD("use %s ") CYAN_BOLD("%s:%d%s ") GREEN_BOLD("%s") " " BLACK_BOLD("%s"),
+             Tags::network(), client->mode(), pool.host().data(), pool.port(), zmq_buf, tlsVersion ? tlsVersion : "", client->ip().data());
 
     const char *fingerprint = client->tlsFingerprint();
     if (fingerprint != nullptr) {
@@ -272,8 +277,13 @@ void xmrig::Network::setJob(IClient *client, const Job &job, bool donate)
         uint64_t diff       = job.diff();;
         const char *scale   = NetworkState::scaleDiff(diff);
 
-        LOG_INFO("%s " MAGENTA_BOLD("new job") " from " WHITE_BOLD("%s:%d") " diff " WHITE_BOLD("%" PRIu64 "%s") " algo " WHITE_BOLD("%s") " height " WHITE_BOLD("%" PRIu64),
-                 Tags::network(), client->pool().host().data(), client->pool().port(), diff, scale, job.algorithm().shortName(), job.height());
+        char zmq_buf[32] = {};
+        if (client->pool().zmq_port() >= 0) {
+            snprintf(zmq_buf, sizeof(zmq_buf), " (ZMQ:%d)", client->pool().zmq_port());
+        }
+
+        LOG_INFO("%s " MAGENTA_BOLD("new job") " from " WHITE_BOLD("%s:%d%s") " diff " WHITE_BOLD("%" PRIu64 "%s") " algo " WHITE_BOLD("%s") " height " WHITE_BOLD("%" PRIu64),
+                 Tags::network(), client->pool().host().data(), client->pool().port(), zmq_buf, diff, scale, job.algorithm().shortName(), job.height());
     }
 
     if (!donate && m_donate) {

From d24581c9633cb60c3c588975a32bc969e7c6a5b3 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sat, 24 Jul 2021 12:27:48 +0700
Subject: [PATCH 6/8] #2492 Add missing --huge-pages-jit command line option.

---
 src/base/kernel/interfaces/IConfig.h | 1 +
 src/core/config/ConfigTransform.cpp  | 3 +++
 src/core/config/Config_platform.h    | 2 ++
 src/core/config/usage.h              | 3 +++
 4 files changed, 9 insertions(+)

diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h
index e531353aa..bc4607865 100644
--- a/src/base/kernel/interfaces/IConfig.h
+++ b/src/base/kernel/interfaces/IConfig.h
@@ -86,6 +86,7 @@ public:
         DnsTtlKey            = 1054,
         SpendSecretKey       = 1055,
         DaemonZMQPortKey     = 1056,
+        HugePagesJitKey      = 1057,
 
         // xmrig common
         CPUPriorityKey       = 1021,
diff --git a/src/core/config/ConfigTransform.cpp b/src/core/config/ConfigTransform.cpp
index c854d982a..1c4f4244a 100644
--- a/src/core/config/ConfigTransform.cpp
+++ b/src/core/config/ConfigTransform.cpp
@@ -195,6 +195,9 @@ void xmrig::ConfigTransform::transform(rapidjson::Document &doc, int key, const
 
     case IConfig::RandomXCacheQoSKey: /* --cache-qos */
         return set(doc, RxConfig::kField, RxConfig::kCacheQoS, true);
+
+    case IConfig::HugePagesJitKey: /* --huge-pages-jit */
+        return set(doc, CpuConfig::kField, CpuConfig::kHugePagesJit, true);
 #   endif
 
 #   ifdef XMRIG_FEATURE_OPENCL
diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h
index 7194f5cc5..c4379d0b4 100644
--- a/src/core/config/Config_platform.h
+++ b/src/core/config/Config_platform.h
@@ -69,6 +69,8 @@ static const option options[] = {
     { "no-huge-pages",         0, nullptr, IConfig::HugePagesKey          },
     { "no-hugepages",          0, nullptr, IConfig::HugePagesKey          },
     { "hugepage-size",         1, nullptr, IConfig::HugePageSizeKey       },
+    { "huge-pages-jit",        0, nullptr, IConfig::HugePagesJitKey       },
+    { "hugepages-jit",         0, nullptr, IConfig::HugePagesJitKey       },
     { "pass",                  1, nullptr, IConfig::PasswordKey           },
     { "print-time",            1, nullptr, IConfig::PrintTimeKey          },
     { "retries",               1, nullptr, IConfig::RetriesKey            },
diff --git a/src/core/config/usage.h b/src/core/config/usage.h
index 4230c9e2f..6cd599c96 100644
--- a/src/core/config/usage.h
+++ b/src/core/config/usage.h
@@ -88,6 +88,9 @@ static inline const std::string &usage()
     u += "      --no-huge-pages           disable huge pages support\n";
 #   ifdef XMRIG_OS_LINUX
     u += "      --hugepage-size=N         custom hugepage size in kB\n";
+#   endif
+#   ifdef XMRIG_ALGO_RANDOMX
+    u += "      --huge-pages-jit          enable huge pages support for RandomX JIT code\n";
 #   endif
     u += "      --asm=ASM                 ASM optimizations, possible values: auto, none, intel, ryzen, bulldozer\n";
 

From 929205536c6d74e3eb86694a79dc15ed8fd19249 Mon Sep 17 00:00:00 2001
From: SChernykh <sergey.v.chernykh@gmail.com>
Date: Sat, 7 Aug 2021 19:38:31 +0200
Subject: [PATCH 7/8] Show the number of transactions in pool job

Useful to check if pool/proxy is working properly and can also be used to compare different pools.
---
 src/base/net/stratum/Job.cpp | 25 +++++++++++++++++++++++++
 src/base/net/stratum/Job.h   |  2 ++
 src/net/Network.cpp          | 12 +++++++++---
 3 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp
index 920bf95f7..7792b904a 100644
--- a/src/base/net/stratum/Job.cpp
+++ b/src/base/net/stratum/Job.cpp
@@ -163,6 +163,31 @@ void xmrig::Job::setSigKey(const char *sig_key)
 }
 
 
+uint32_t xmrig::Job::getNumTransactions() const
+{
+    if (m_algorithm.family() > Algorithm::RANDOM_X) {
+        return 0;
+    }
+
+    uint32_t num_transactions = 0;
+
+    // Monero (and some other coins) has the number of transactions encoded as varint in the end of hashing blob
+    const size_t expected_tx_offset = (m_algorithm == Algorithm::RX_WOW) ? 141 : 75;
+
+    if ((m_size > expected_tx_offset) && (m_size <= expected_tx_offset + 4)) {
+        for (size_t i = expected_tx_offset, k = 0; i < m_size; ++i, k += 7) {
+            const uint8_t b = m_blob[i];
+            num_transactions |= static_cast<uint32_t>(b & 0x7F) << k;
+            if ((b & 0x80) == 0) {
+                break;
+            }
+        }
+    }
+
+    return num_transactions;
+}
+
+
 void xmrig::Job::copy(const Job &other)
 {
     m_algorithm  = other.m_algorithm;
diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h
index c72902756..414da8fea 100644
--- a/src/base/net/stratum/Job.h
+++ b/src/base/net/stratum/Job.h
@@ -139,6 +139,8 @@ public:
 
     inline bool hasMinerSignature() const { return m_hasMinerSignature; }
 
+    uint32_t getNumTransactions() const;
+
 private:
     void copy(const Job &other);
     void move(Job &&other);
diff --git a/src/net/Network.cpp b/src/net/Network.cpp
index 64a1b689e..d1acb4ce4 100644
--- a/src/net/Network.cpp
+++ b/src/net/Network.cpp
@@ -274,7 +274,7 @@ void xmrig::Network::setJob(IClient *client, const Job &job, bool donate)
     if (!BenchState::size())
 #   endif
     {
-        uint64_t diff       = job.diff();;
+        uint64_t diff       = job.diff();
         const char *scale   = NetworkState::scaleDiff(diff);
 
         char zmq_buf[32] = {};
@@ -282,8 +282,14 @@ void xmrig::Network::setJob(IClient *client, const Job &job, bool donate)
             snprintf(zmq_buf, sizeof(zmq_buf), " (ZMQ:%d)", client->pool().zmq_port());
         }
 
-        LOG_INFO("%s " MAGENTA_BOLD("new job") " from " WHITE_BOLD("%s:%d%s") " diff " WHITE_BOLD("%" PRIu64 "%s") " algo " WHITE_BOLD("%s") " height " WHITE_BOLD("%" PRIu64),
-                 Tags::network(), client->pool().host().data(), client->pool().port(), zmq_buf, diff, scale, job.algorithm().shortName(), job.height());
+        char tx_buf[32] = {};
+        const uint32_t num_transactions = job.getNumTransactions();
+        if (num_transactions > 0) {
+            snprintf(tx_buf, sizeof(tx_buf), " (%u tx)", num_transactions);
+        }
+
+        LOG_INFO("%s " MAGENTA_BOLD("new job") " from " WHITE_BOLD("%s:%d%s") " diff " WHITE_BOLD("%" PRIu64 "%s") " algo " WHITE_BOLD("%s") " height " WHITE_BOLD("%" PRIu64) "%s",
+                 Tags::network(), client->pool().host().data(), client->pool().port(), zmq_buf, diff, scale, job.algorithm().shortName(), job.height(), tx_buf);
     }
 
     if (!donate && m_donate) {

From 08d79ddcdc350e5531ea9d27a2815529250cb467 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sun, 8 Aug 2021 19:36:54 +0700
Subject: [PATCH 8/8] v6.14.0-dev

---
 CHANGELOG.md  | 6 ++++++
 src/version.h | 6 +++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d1f4b4612..4482cc44e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# v6.14.0
+- [#2484](https://github.com/xmrig/xmrig/pull/2484) Added ZeroMQ support for solo mining.
+- [#2476](https://github.com/xmrig/xmrig/issues/2476) Fixed crash in DMI memory reader.
+- [#2492](https://github.com/xmrig/xmrig/issues/2492) Added missing `--huge-pages-jit` command line option.
+- [#2512](https://github.com/xmrig/xmrig/pull/2512) Added show the number of transactions in pool job.
+
 # v6.13.1
 - [#2468](https://github.com/xmrig/xmrig/pull/2468) Fixed regression in previous version: don't send miner signature during regular mining.
 
diff --git a/src/version.h b/src/version.h
index 5dfc7b2b7..3d523fee4 100644
--- a/src/version.h
+++ b/src/version.h
@@ -28,15 +28,15 @@
 #define APP_ID        "xmrig"
 #define APP_NAME      "XMRig"
 #define APP_DESC      "XMRig miner"
-#define APP_VERSION   "6.13.2-dev"
+#define APP_VERSION   "6.14.0-dev"
 #define APP_DOMAIN    "xmrig.com"
 #define APP_SITE      "www.xmrig.com"
 #define APP_COPYRIGHT "Copyright (C) 2016-2021 xmrig.com"
 #define APP_KIND      "miner"
 
 #define APP_VER_MAJOR  6
-#define APP_VER_MINOR  13
-#define APP_VER_PATCH  2
+#define APP_VER_MINOR  14
+#define APP_VER_PATCH  0
 
 #ifdef _MSC_VER
 #   if (_MSC_VER >= 1920)