From 30a6ca44479d520a7f7d044b905aed78e54c7247 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 9 Jul 2021 18:47:15 -0400 Subject: [PATCH 1/7] Built-in stratum server --- src/Makefile.am | 2 + src/chainparamsbase.h | 1 + src/init.cpp | 14 + src/rpc/register.h | 6 +- src/stratum.cpp | 2132 +++++++++++++++++++++++++++++++++++++++++ src/stratum.h | 21 + 6 files changed, 2175 insertions(+), 1 deletion(-) create mode 100644 src/stratum.cpp create mode 100644 src/stratum.h diff --git a/src/Makefile.am b/src/Makefile.am index 0711b52cc..abdf0f874 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -203,6 +203,7 @@ BITCOIN_CORE_H = \ script/standard.h \ serialize.h \ streams.h \ + stratum.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ support/cleanse.h \ @@ -315,6 +316,7 @@ libbitcoin_server_a_SOURCES = \ rpc/server.cpp \ script/serverchecker.cpp \ script/sigcache.cpp \ + stratum.cpp \ timedata.cpp \ torcontrol.cpp \ txdb.cpp \ diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 9379e95b4..1fbdec8bd 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -40,6 +40,7 @@ public: const std::string& DataDir() const { return strDataDir; } int RPCPort() const { return nRPCPort; } + int StratumPort() const { return (RPCPort() + 1000); } protected: CBaseChainParams() {} diff --git a/src/init.cpp b/src/init.cpp index 01916ee76..acce70062 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -36,6 +36,7 @@ #include "httprpc.h" #include "key.h" #include "notarizationdb.h" +#include "stratum.h" #ifdef ENABLE_MINING #include "key_io.h" @@ -89,6 +90,7 @@ using namespace std; #include "hush_defs.h" +static const bool DEFAULT_STRATUM_ENABLE = false; extern void ThreadSendAlert(); extern bool hush_dailysnapshot(int32_t height); extern int32_t HUSH_LOADINGBLOCKS; @@ -187,6 +189,7 @@ static boost::scoped_ptr globalVerifyHandle; void Interrupt(boost::thread_group& threadGroup) { + InterruptStratumServer(); InterruptHTTPServer(); InterruptHTTPRPC(); InterruptRPC(); @@ -218,6 +221,7 @@ void Shutdown() StopHTTPRPC(); StopREST(); StopRPC(); + StopStratumServer(); StopHTTPServer(); #ifdef ENABLE_WALLET if (pwalletMain) @@ -582,6 +586,13 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-metricsui", _("Set to 1 for a persistent metrics screen, 0 for sequential metrics output (default: 1 if running in a console, 0 otherwise)")); strUsage += HelpMessageOpt("-metricsrefreshtime", strprintf(_("Number of seconds between metrics refreshes (default: %u if running in a console, %u otherwise)"), 1, 600)); } + + strUsage += HelpMessageGroup(_("Stratum server options:")); + strUsage += HelpMessageOpt("-stratum", _("Enable stratum server (default: off)")); + strUsage += HelpMessageOpt("-stratumbind=", _("Bind to given address to listen for Stratum work requests. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)")); + strUsage += HelpMessageOpt("-stratumport=", strprintf(_("Listen for Stratum work requests on (default: %u or testnet: %u)"), BaseParams().StratumPort(), BaseParams().StratumPort())); + strUsage += HelpMessageOpt("-stratumallowip=", _("Allow Stratum work requests from specified source. Valid for are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times")); + strUsage += HelpMessageGroup(_("Hush Smart Chain options:")); strUsage += HelpMessageOpt("-ac_algo", _("Choose PoW mining algorithm, default is Equihash")); strUsage += HelpMessageOpt("-ac_blocktime", _("Block time in seconds, default is 60")); @@ -941,10 +952,13 @@ static void ZC_LoadParams( bool AppInitServers(boost::thread_group& threadGroup) { + fprintf(stderr,"%s: start\n",__func__); RPCServer::OnStopped(&OnRPCStopped); RPCServer::OnPreCommand(&OnRPCPreCommand); if (!InitHTTPServer()) return false; + if (GetBoolArg("-stratum", DEFAULT_STRATUM_ENABLE) && !InitStratumServer()) + return false; if (!StartRPC()) return false; if (!StartHTTPRPC()) diff --git a/src/rpc/register.h b/src/rpc/register.h index 2493e447b..7c545da48 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -1,7 +1,7 @@ +// Copyright (c) 2016-2021 The Hush developers // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html - /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -38,6 +38,9 @@ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); /** Register test transaction RPC commands */ void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC); +/** Register stratum RPC commands */ +void RegisterStratumRPCCommands(CRPCTable &tableRPC); + static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) { @@ -49,6 +52,7 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) #ifdef TESTMODE RegisterTesttransactionsRPCCommands(tableRPC); #endif + RegisterStratumRPCCommands(tableRPC); } #endif diff --git a/src/stratum.cpp b/src/stratum.cpp new file mode 100644 index 000000000..3619c9ea1 --- /dev/null +++ b/src/stratum.cpp @@ -0,0 +1,2132 @@ +// Copyright (c) 2021-2021 The Hush developers +// Copyright (c) 2020-2021 The Freicoin Developers +// Copyright (c) 2021-2021 Decker +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// Stratum protocol: +// - https://en.bitcoin.it/wiki/Stratum_mining_protocol - Stratum mining protocol +// - https://github.com/slushpool/poclbm-zcash/wiki/Stratum-protocol-changes-for-ZCash - Stratum protocol changes for ZCash + +#include "stratum.h" +#include "base58.h" +#include "chainparams.h" +#include "consensus/validation.h" +#include "crypto/sha256.h" +#include "httpserver.h" +#include "miner.h" +#include "netbase.h" +#include "net.h" +#include "rpc/server.h" +#include "serialize.h" +#include "streams.h" +#include "sync.h" +#include "txmempool.h" +#include "uint256.h" +#include "util.h" +#include "utilstrencodings.h" +#include +#include // for std::reverse +#include +#include + +#include // for boost::trim +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#ifdef WIN32 +#include +#else +#include +#include +#include +#endif + +#include "main.h" // cs_main +#include +#include "ui_interface.h" +#include // make_unique + +#include +#include + +#include +#include + +// https://en.cppreference.com/w/cpp/types/integer - cinttypes for format constants, like PRId64, etc. +#include + + +extern uint16_t ASSETCHAINS_RPCPORT; // don't want to include komodo_globals.h +UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); // rpc/blockchain.cpp +bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); // src/core_read.cpp + +static const long jobRebroadcastTimeout = 30; +static const long txMemPoolCheckTimeout = 10; + +/** + * Begin of helper routines, + * included: missed in httpserver.cpp in our codebase, missed + * constructors for CSubNet(...), etc. +*/ + +namespace { // better to use anonymous namespace for helper routines + + class CStratumParams { + private: + const arith_uint256 defaultHashTarget; + arith_uint256 currentHashTarget; + public: + CStratumParams() : defaultHashTarget(UintToArith256(uint256S("00ffff0000000000000000000000000000000000000000000000000000000000"))), + currentHashTarget(defaultHashTarget), fAllowLowDiffShares(false), fCheckEquihashSolution(true), + fstdErrDebugOutput(false) { } + ~CStratumParams() {} + + void setTarget(const arith_uint256& target) { currentHashTarget = target; } + arith_uint256 getTarget() { return currentHashTarget; } + + bool fAllowLowDiffShares; + bool fCheckEquihashSolution; + bool fstdErrDebugOutput; + + } instance_of_cstratumparams; + + /** Check if a network address is allowed to access the Stratum server */ + static bool ClientAllowed(const std::vector& allowed_subnets, const CNetAddr& netaddr) + { + if (!netaddr.IsValid()) + return false; + for(const CSubNet& subnet : allowed_subnets) + if (subnet.Match(netaddr)) + return true; + return false; + } + + /** Initialize ACL list for Stratum server */ + static bool InitStratumAllowList(std::vector& allowed_subnets) + { + allowed_subnets.clear(); + allowed_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet + allowed_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost + if (mapMultiArgs.count("-stratumallowip")) { + const std::vector& vAllow = mapMultiArgs["-stratumallowip"]; + for(const std::string& strAllow : vAllow) { + CSubNet subnet(strAllow); + if (!subnet.IsValid()) { + uiInterface.ThreadSafeMessageBox( + strprintf("Invalid -stratumallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow), + "", CClientUIInterface::MSG_ERROR); + return false; + } + allowed_subnets.push_back(subnet); + } + } + return true; + } + + double GetDifficultyFromBits(uint32_t bits) { + + uint32_t powLimit = UintToArith256(Params().GetConsensus().powLimit).GetCompact(); + int nShift = (bits >> 24) & 0xff; + int nShiftAmount = (powLimit >> 24) & 0xff; + + double dDiff = + (double)(powLimit & 0x00ffffff) / + (double)(bits & 0x00ffffff); + + while (nShift < nShiftAmount) + { + dDiff *= 256.0; + nShift++; + } + while (nShift > nShiftAmount) + { + dDiff /= 256.0; + nShift--; + } + + return dDiff; + } + + std::string DateTimeStrPrecise() // or we can use standart one, like DateTimeStrFormat("[%Y-%m-%d %H:%M:%S.%f]", GetTime()) + { + // https://stackoverflow.com/questions/28136660/format-a-posix-time-with-just-3-digits-in-fractional-seconds + // https://www.boost.org/doc/libs/1_35_0/doc/html/date_time/date_time_io.html#date_time.format_flags + + // std::locale takes ownership of the pointer + boost::posix_time::ptime const date_time = boost::posix_time::microsec_clock::local_time(); + std::locale loc(std::locale::classic(), new boost::posix_time::time_facet("[%Y-%m-%d %H:%M:%S.%f] ")); + std::stringstream ss; + ss.imbue(loc); + // ss << boost::posix_time::from_time_t(nTime); + ss << date_time; + return ss.str(); + } + + std::string get_stripped_username(const std::string& username) { + std::string res(username); + size_t dotpos = username.find('.'); + if (dotpos != std::string::npos) + res.resize(dotpos); + return res; + } + + // C++11 map initialization + const std::map mapColors = { + { "cl_N", "\x1B[0m" }, + { "cl_RED", "\x1B[31m" }, + { "cl_GRN", "\x1B[32m" }, + { "cl_YLW", "\x1B[33m" }, + { "cl_BLU", "\x1B[34m" }, + { "cl_MAG", "\x1B[35m" }, + { "cl_CYN", "\x1B[36m" }, + { "cl_BLK", "\x1B[22;30m" }, /* black */ + { "cl_RD2", "\x1B[22;31m" }, /* red */ + { "cl_GR2", "\x1B[22;32m" }, /* green */ + { "cl_YL2", "\x1B[22;33m" }, /* dark yellow */ + { "cl_BL2", "\x1B[22;34m" }, /* blue */ + { "cl_MA2", "\x1B[22;35m" }, /* magenta */ + { "cl_CY2", "\x1B[22;36m" }, /* cyan */ + { "cl_SIL", "\x1B[22;37m" }, /* gray */ + { "cl_LRD", "\x1B[01;31m" }, /* light red */ + { "cl_LGR", "\x1B[01;32m" }, /* light green */ + { "cl_LYL", "\x1B[01;33m" }, /* tooltips */ + { "cl_LBL", "\x1B[01;34m" }, /* light blue */ + { "cl_LMA", "\x1B[01;35m" }, /* light magenta */ + { "cl_LCY", "\x1B[01;36m" }, /* light cyan */ + { "cl_WHT", "\x1B[01;37m" }, /* white */ + }; + + enum ColorType + { + cl_N, cl_RED, cl_GRN, cl_YLW, cl_BLU, cl_MAG, cl_CYN, cl_BLK, cl_RD2, cl_GR2, cl_YL2, cl_BL2, cl_MA2, cl_CY2, cl_SIL, cl_LRD, cl_LGR, cl_LYL, cl_LBL, cl_LMA, cl_LCY, cl_WHT + }; + + char const* ColorTypeNames[]= + { + "\x1B[0m" , "\x1B[31m" , "\x1B[32m" , "\x1B[33m" , "\x1B[34m" , + "\x1B[35m" , "\x1B[36m" , "\x1B[22;30m", "\x1B[22;31m", "\x1B[22;32m", + "\x1B[22;33m", "\x1B[22;34m", "\x1B[22;35m", "\x1B[22;36m", "\x1B[22;37m", + "\x1B[01;31m", "\x1B[01;32m", "\x1B[01;33m", "\x1B[01;34m", "\x1B[01;35m", + "\x1B[01;36m", "\x1B[01;37m" + }; +} + +namespace ccminer { + + bool hex2bin(void *output, const char *hexstr, size_t len) + { + unsigned char *p = (unsigned char *) output; + char hex_byte[4]; + char *ep; + + hex_byte[2] = '\0'; + + while (*hexstr && len) { + if (!hexstr[1]) { + LogPrint("stratum", "hex2bin str truncated"); + return false; + } + hex_byte[0] = hexstr[0]; + hex_byte[1] = hexstr[1]; + *p = (unsigned char) strtol(hex_byte, &ep, 16); + if (*ep) { + LogPrint("stratum", "hex2bin failed on '%s'", hex_byte); + return false; + } + p++; + hexstr += 2; + len--; + } + + return (len == 0 && *hexstr == 0) ? true : false; + } + + // equi/equi-stratum.cpp + double target_to_diff_equi(uint32_t* target) + { + unsigned char* tgt = (unsigned char*) target; + uint64_t m = + (uint64_t)tgt[30] << 24 | + (uint64_t)tgt[29] << 16 | + (uint64_t)tgt[28] << 8 | + (uint64_t)tgt[27] << 0; + + if (!m) + return 0.; + else + return (double)0xffff0000UL/m; + } + + void diff_to_target_equi(uint32_t *target, double diff) + { + uint64_t m; + int k; + + for (k = 6; k > 0 && diff > 1.0; k--) + diff /= 4294967296.0; + m = (uint64_t)(4294901760.0 / diff); + if (m == 0 && k == 6) + memset(target, 0xff, 32); + else { + memset(target, 0, 32); + target[k + 1] = (uint32_t)(m >> 8); + target[k + 2] = (uint32_t)(m >> 40); + //memset(target, 0xff, 6*sizeof(uint32_t)); + for (k = 0; k < 28 && ((uint8_t*)target)[k] == 0; k++) + ((uint8_t*)target)[k] = 0xff; + } + } + + void kmd_diff_to_target_equi(uint32_t *target, double diff) + { + uint64_t m; + int k; + + for (k = 6; k > 0 && diff > 1.0; k--) + diff /= (double)((uint64_t)0x100000000); + m = (uint64_t)((uint64_t)0x0f0f0f0f / diff); + if (m == 0 && k == 6) + memset(target, 0xff, 32); + else { + memset(target, 0, 32); + target[k + 1] = (uint32_t)(m >> 8); + target[k + 2] = (uint32_t)(m >> 40); + //memset(target, 0xff, 6*sizeof(uint32_t)); + + for (k = 0; k < 28 && ((uint8_t*)target)[k] == 0; k++) + ((uint8_t*)target)[k] = 0xff; + for (k = 0; k < 32; k++) ((uint8_t*)target)[31-k] = ((uint8_t*)target)[31-k-1]; + ((uint8_t*)target)[0] = 0xff; + } + } + + /* compute nbits to get the network diff */ + double equi_network_diff(uint32_t nbits) + { + //KMD bits: "1e 015971", + //KMD target: "00 00 015971000000000000000000000000000000000000000000000000000000", + //KMD bits: "1d 686aaf", + //KMD target: "00 0000 686aaf0000000000000000000000000000000000000000000000000000", + // uint32_t nbits = work->data[26]; + + uint32_t bits = (nbits & 0xffffff); + int16_t shift = (/*swab32*/bswap_32(nbits) & 0xff); + shift = (31 - shift) * 8; // 8 bits shift for 0x1e, 16 for 0x1d + uint64_t tgt64 = /*swab32*/bswap_32(bits); + tgt64 = tgt64 << shift; + // applog_hex(&tgt64, 8); + uint8_t net_target[32] = { 0 }; + for (int b=0; b<8; b++) + net_target[31-b] = ((uint8_t*)&tgt64)[b]; + // applog_hex(net_target, 32); + double d = target_to_diff_equi((uint32_t*)net_target); + return d; + } + + double equi_stratum_target_to_diff(const std::string& target) + { + uint8_t target_bin[32], target_be[32]; + + const char *target_hex = target.c_str(); + if (!target_hex || strlen(target_hex) == 0) + return false; + + hex2bin(target_bin, target_hex, 32); + memset(target_be, 0xff, 32); + int filled = 0; + for (int i=0; i<32; i++) { + if (filled == 3) break; + target_be[31-i] = target_bin[i]; + if (target_bin[i]) filled++; + } + + double d = target_to_diff_equi((uint32_t*) &target_be); + return d; + } + + /* Subtract the `struct timeval' values X and Y, + storing the result in RESULT. + Return 1 if the difference is negative, otherwise 0. */ + int timeval_subtract(struct timeval *result, struct timeval *x, + struct timeval *y) + { + /* Perform the carry for the later subtraction by updating Y. */ + if (x->tv_usec < y->tv_usec) { + int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_usec - y->tv_usec > 1000000) { + int nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + * `tv_usec' is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; + } + +} + +/** + * End of helper routines +*/ + +struct StratumClient +{ + evconnlistener* m_listener; + evutil_socket_t m_socket; + bufferevent* m_bev; + CService m_from; + int m_nextid; + uint256 m_secret; + + CService GetPeer() const + { return m_from; } + + std::string m_client; + + bool m_authorized; + CBitcoinAddress m_addr; + double m_mindiff; + + uint32_t m_version_rolling_mask; + + CBlockIndex* m_last_tip; + bool m_second_stage; + bool m_send_work; + + bool m_supports_aux; + std::set m_aux_addr; + + bool m_supports_extranonce; + + StratumClient() : m_listener(0), m_socket(0), m_bev(0), m_nextid(0), m_authorized(false), m_mindiff(0.0), m_version_rolling_mask(0x00000000), m_last_tip(0), m_second_stage(false), m_send_work(false), m_supports_aux(false), m_supports_extranonce(false) { GenSecret(); } + StratumClient(evconnlistener* listener, evutil_socket_t socket, bufferevent* bev, CService from) : m_listener(listener), m_socket(socket), m_bev(bev), m_nextid(0), m_from(from), m_authorized(false), m_mindiff(0.0), m_version_rolling_mask(0x00000000), m_last_tip(0), m_second_stage(false), m_send_work(false), m_supports_aux(false), m_supports_extranonce(false) { GenSecret(); } + + void GenSecret(); + std::vector ExtraNonce1(uint256 job_id) const; +}; + +void StratumClient::GenSecret() +{ + GetRandBytes(m_secret.begin(), 32); +} + +std::vector StratumClient::ExtraNonce1(uint256 job_id) const +{ + CSHA256 nonce_hasher; + nonce_hasher.Write(m_secret.begin(), 32); + + if (m_supports_extranonce) { + nonce_hasher.Write(job_id.begin(), 32); + } + + uint256 job_nonce; + nonce_hasher.Finalize(job_nonce.begin()); + return {job_nonce.begin(), job_nonce.begin()+8}; +} + +struct StratumWork { + CBlockTemplate m_block_template; + // First we generate the segwit commitment for the miner's coinbase with + // ComputeFastMerkleBranch. + std::vector m_cb_wit_branch; + // Then we compute the initial right-branch for the block-tx Merkle tree + // using ComputeStableMerkleBranch... + std::vector m_bf_branch; + // ...which is appended to the end of m_cb_branch so we can compute the + // block's hashMerkleRoot with ComputeMerkleBranch. + std::vector m_cb_branch; + bool m_is_witness_enabled; + + int32_t nHeight; + std::string local_diff; + + // The cached 2nd-stage auxiliary hash value, if an auxiliary proof-of-work + // solution has been found. + boost::optional m_aux_hash2; + + StratumWork() : m_is_witness_enabled(false),nHeight(0) { }; + StratumWork(const CBlockTemplate& block_template, bool is_witness_enabled); + + CBlock& GetBlock() + { return m_block_template.block; } + const CBlock& GetBlock() const + { return m_block_template.block; } +}; + +StratumWork::StratumWork(const CBlockTemplate& block_template, bool is_witness_enabled) + : m_block_template(block_template) + , m_is_witness_enabled(is_witness_enabled), nHeight(0) +{ + // Generate the block-witholding secret for the work unit. + + std::vector leaves; + for (const auto& tx : m_block_template.block.vtx) { + leaves.push_back(tx.GetHash()); + } + + std::vector vMerkleTree; uint256 merkleRoot; bool fMutated; + + merkleRoot = BuildMerkleTree(&fMutated, leaves, vMerkleTree); + m_cb_branch = GetMerkleBranch(0, leaves.size(), vMerkleTree); + // m_cb_branch = ComputeMerkleBranch(leaves, 0); +}; + +//! Critical seciton guarding access to any of the stratum global state +static CCriticalSection cs_stratum; + +//! List of subnets to allow stratum connections from +static std::vector stratum_allow_subnets; + +//! Bound stratum listening sockets +static std::map bound_listeners; + +//! Active miners connected to us +static std::map subscriptions; + +//! Mapping of stratum method names -> handlers +static std::map > stratum_method_dispatch; + +//! A mapping of job_id -> work templates +static std::map work_templates; + +//! The job_id of the first work unit to have its auxiliary proof-of-work solved +//! for the current block, or boost::none if no solution has been returned yet. +static boost::optional half_solved_work; + +//! A thread to watch for new blocks and send mining notifications +static boost::thread block_watcher_thread; + +std::string HexInt4(uint32_t val) +{ + std::vector vch; + vch.push_back((val >> 24) & 0xff); + vch.push_back((val >> 16) & 0xff); + vch.push_back((val >> 8) & 0xff); + vch.push_back( val & 0xff); + return HexStr(vch); +} + +uint32_t ParseHexInt4(const UniValue& hex, const std::string& name) +{ + std::vector vch = ParseHexV(hex, name); + if (vch.size() != 4) { + throw JSONRPCError(RPC_INVALID_PARAMETER, name+" must be exactly 4 bytes / 8 hex"); + } + uint32_t ret = 0; + ret |= vch[0] << 24; + ret |= vch[1] << 16; + ret |= vch[2] << 8; + ret |= vch[3]; + return ret; +} + +uint256 ParseUInt256(const UniValue& hex, const std::string& name) +{ + if (!hex.isStr()) { + throw std::runtime_error(name+" must be a hexidecimal string"); + } + std::vector vch = ParseHex(hex.get_str()); + if (vch.size() != 32) { + throw std::runtime_error(name+" must be exactly 32 bytes / 64 hex"); + } + uint256 ret; + std::copy(vch.begin(), vch.end(), ret.begin()); + return ret; +} + +static double ClampDifficulty(const StratumClient& client, double diff) +{ + if (client.m_mindiff > 0) { + diff = client.m_mindiff; + } + diff = std::max(diff, 0.001); + return diff; +} + +static std::string GetExtraNonceRequest(StratumClient& client, const uint256& job_id) +{ + // https://en.bitcoin.it/wiki/Stratum_mining_protocol#mining.set_extranonce + // mining.set_extranonce("extranonce1", extranonce2_size) + + std::string ret; + if (client.m_supports_extranonce) + { + std::vector extranonce1 = client.ExtraNonce1(job_id); + + const std::string k_extranonce_req = std::string() + + "{" + + "\"id\":"; + const std::string k_extranonce_req2 = std::string() + + "," + + "\"method\":\"mining.set_extranonce\"," + + "\"params\":[" + + "\""; + const std::string k_extranonce_req3 = std::string() + + "\"," // extranonce1 + + strprintf("%d", 32 - extranonce1.size()) // extranonce2.size() = 32 - extranonce1.size() + + "]" + + "}" + + "\n"; + + ret = k_extranonce_req + + strprintf("%d", client.m_nextid++) + + k_extranonce_req2 + + HexStr(extranonce1) + + k_extranonce_req3; + } + return ret; +} + +/** + * @brief + * + * @param client + * @param current_work + * @param addr + * @param extranonce1 + * @param extranonce2 + * @param cb + * @param bf + * @param cb_branch + */ +void CustomizeWork(const StratumClient& client, const StratumWork& current_work, const CBitcoinAddress& addr, const std::vector& extranonce1, const std::vector& extranonce2, CMutableTransaction& cb, CMutableTransaction& bf, std::vector& cb_branch) +{ + if (current_work.GetBlock().vtx.empty()) { + const std::string msg = strprintf("%s: no transactions in block template; unable to submit work", __func__); + LogPrint("stratum", "%s\n", msg); + throw std::runtime_error(msg); + } + + cb = CMutableTransaction(current_work.GetBlock().vtx[0]); + + if (cb.vin.size() != 1) { + const std::string msg = strprintf("%s: unexpected number of inputs; is this even a coinbase transaction?", __func__); + LogPrint("stratum", "%s\n", msg); + throw std::runtime_error(msg); + } + + std::vector nonce(extranonce1); + + if ((nonce.size() + extranonce2.size()) != 32) { + const std::string msg = strprintf("%s: unexpected combined nonce length: extranonce1(%d) + extranonce2(%d) != 32; unable to submit work", __func__, nonce.size(), extranonce2.size()); + LogPrint("stratum", "%s\n", msg); + throw std::runtime_error(msg); + } + nonce.insert(nonce.end(), extranonce2.begin(), extranonce2.end()); + + // nonce = extranonce1 + extranonce2 + // if (instance_of_cstratumparams.fstdErrDebugOutput) { + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " nonce = " << HexStr(nonce) << std::endl; + // } + + if (cb.vin.empty()) { + const std::string msg = strprintf("%s: first transaction is missing coinbase input; unable to customize work to miner", __func__); + LogPrint("stratum", "%s\n", msg); + throw std::runtime_error(msg); + } + // cb.vin[0].scriptSig = + // CScript() + // << cb.lock_height + // << nonce; + + /* actually we will change only cb destination on the miner address */ + { + if (cb.vout.empty()) { + const std::string msg = strprintf("%s: coinbase transaction is missing outputs; unable to customize work to miner", __func__); + LogPrint("stratum", "%s\n", msg); + throw std::runtime_error(msg); + } + if (cb.vout[0].scriptPubKey == (CScript() << OP_FALSE)) { + cb.vout[0].scriptPubKey = GetScriptForDestination(addr.Get()); + } + } + + // cb_branch = current_work.m_cb_branch; +} + +std::string GetWorkUnit(StratumClient& client) +{ + // LOCK(cs_main); + + /* if (!g_connman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + } */ + + /* if (!Params().MineBlocksOnDemand() && g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) { + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Komodo is not connected!"); + } */ + + bool fvNodesEmpty; + { + LOCK(cs_vNodes); + fvNodesEmpty = vNodes.empty(); + } + + if (Params().MiningRequiresPeers() && fvNodesEmpty) + { + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Komodo is not connected!"); + } + + if (IsInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Komodo is downloading blocks..."); + } + + if (!client.m_authorized && client.m_aux_addr.empty()) { + throw JSONRPCError(RPC_INVALID_REQUEST, "Stratum client not authorized. Use mining.authorize first, with a Komodo address as the username."); + } + + static CBlockIndex* tip = NULL; // pindexPrev + static uint256 job_id; + static unsigned int transactions_updated_last = 0; + static int64_t last_update_time = 0; + + // rpc/mining.cpp -> getblocktemplate -> Update block + if ( tip != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != transactions_updated_last && (GetTime() - last_update_time) > 5) || + !work_templates.count(job_id)) + { + CBlockIndex *tip_new = chainActive.Tip(); + + /** + * We will check script later inside CustomizeWork, if it will be == CScript() << OP_FALSE it will mean + * that work need to be customized, and in that case cb.vout[0],scriptPubKey will be set to GetScriptForDestination(addr.Get()) . + * In other words to the address with which stratum client is authorized. + */ + const CScript scriptDummy = CScript() << OP_FALSE; + std::unique_ptr new_work(CreateNewBlock(CPubKey(), scriptDummy, HUSH_MAXGPUCOUNT, false)); // std::unique_ptr new_work = BlockAssembler(Params()).CreateNewBlock(script); + + /* test values for debug */ + // new_work->block.nBits = 0x200f0f0f; + // new_work->block.nTime = 1623567886; + // new_work->block.hashPrevBlock = uint256S("027e3758c3a65b12aa1046462b486d0a63bfa1beae327897f56c5cfb7daaae71"); + // new_work->block.hashMerkleRoot = uint256S("29f0e769c762b691d81d31bbb603719a94ef04d53d332f7de5e5533ddfd08e19"); + // new_work->block.hashFinalSaplingRoot = uint256S("3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb"); + // DecodeHexTx(new_work->block.vtx[0], "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff01aa2ce73b0000000023210325b4ca6736f90679f712be1454c5302050aae6edb51b0d2a051156bc868fec16ac4aabc560"); + + if (!new_work) { + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + } + + // if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << "hashMerkleRoot = " << new_work->block.hashMerkleRoot.ToString() << std::endl; + + // So that block.GetHash() is correct + //new_work->block.hashMerkleRoot = BlockMerkleRoot(new_work->block); + new_work->block.hashMerkleRoot = new_work->block.BuildMerkleTree(); + + // NB! here we have merkle with scriptDummy script in coinbase, after CustomizeWork we should recalculate it (!) + // if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << "hashMerkleRoot = " << new_work->block.hashMerkleRoot.ToString() << std::endl; + + job_id = new_work->block.GetHash(); + //work_templates[job_id] = StratumWork(*new_work, new_work->block.vtx[0]->HasWitness()); + work_templates[job_id] = StratumWork(*new_work, false); + + tip = tip_new; + + transactions_updated_last = mempool.GetTransactionsUpdated(); + last_update_time = GetTime(); + + LogPrint("stratum", "New stratum block template (%d total): %s\n", work_templates.size(), HexStr(job_id.begin(), job_id.end())); + + // Remove any old templates + std::vector old_job_ids; + boost::optional oldest_job_id = boost::none; + uint32_t oldest_job_nTime = last_update_time; + for (const auto& work_template : work_templates) { + // If, for whatever reason the new work was generated with + // an old nTime, don't erase it! + if (work_template.first == job_id) { + continue; + } + // Build a list of outdated work units to free. + if (work_template.second.GetBlock().nTime < (last_update_time - 900)) { + old_job_ids.push_back(work_template.first); + } + // Track the oldest work unit, in case we have too much + // recent work. + if (work_template.second.GetBlock().nTime <= oldest_job_nTime) { + oldest_job_id = work_template.first; + oldest_job_nTime = work_template.second.GetBlock().nTime; + } + } + // Remove all outdated work. + for (const auto& old_job_id : old_job_ids) { + work_templates.erase(old_job_id); + LogPrint("stratum", "Removed outdated stratum block template (%d total): %s\n", work_templates.size(), HexStr(old_job_id.begin(), old_job_id.end())); + } + // Remove the oldest work unit if we're still over the maximum + // number of stored work templates. + if (work_templates.size() > 30 && oldest_job_id) { + work_templates.erase(oldest_job_id.get()); + LogPrint("stratum", "Removed oldest stratum block template (%d total): %s\n", work_templates.size(), HexStr(oldest_job_id.get().begin(), oldest_job_id.get().end())); + } + } + + StratumWork& current_work = work_templates[job_id]; + + + CBlockIndex tmp_index; + + // Native proof-of-work difficulty + tmp_index.nBits = current_work.GetBlock().nBits; + double diff = ClampDifficulty(client, GetDifficulty(&tmp_index)); + + UniValue set_target(UniValue::VOBJ); + set_target.push_back(Pair("id", client.m_nextid++)); + set_target.push_back(Pair("method", "mining.set_target")); + UniValue set_target_params(UniValue::VARR); + + std::string strTarget; // set_target + { + arith_uint256 hashTarget; bool fNegative,fOverflow; + /* + + // Targets Table Example: komodo diff and ccminer diff are different (!), + // Komodo diff = NiceHash diff, ccminer_diff = Yiimp diff. + + hashTarget.SetCompact(HUSH_MINDIFF_NBITS,&fNegative,&fOverflow); // blkhdr.nBits + hashTarget = UintToArith256(uint256S("0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f")); + hashTarget.SetHex("0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); + + hashTarget.SetHex("00ffff0000000000000000000000000000000000000000000000000000000000"); // komodo_diff = 15.0591, ccminer_diff = 1 + hashTarget.SetHex("003fffc000000000000000000000000000000000000000000000000000000000"); // komodo_diff = 60.2362, ccminer_diff = 4 + hashTarget.SetHex("0007fff800000000000000000000000000000000000000000000000000000000"); // komodo_diff = 481.89, ccminer_diff = 31.9999 + hashTarget.SetHex("c7ff3800ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // komodo_diff = 0.0752956, ccminer_diff = 1.00303 + hashTarget.SetHex("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); // komodo_diff = 1, ccminer_diff = 16.9956 + */ + + arith_uint256 aHashTarget = instance_of_cstratumparams.getTarget(); + + // arith_uint256 aHashTarget = UintToArith256(uint256S("00ffff0000000000000000000000000000000000000000000000000000000000")); // 1.0 + // aHashTarget = aHashTarget / 8704; // komodo_diff = 131074 (NiceHash), ccminer_diff = 8704 (Yiimp) + + /* here we can adjust diff by some algo, note that 00ffff0000000000000000000000000000000000000000000000000000000000 / 8704 = + 0000078780000000000000000000000000000000000000000000000000000000, which is equivalent to komodo_diff = 131074 (NiceHash), + ccminer_diff = 8704 (Yiimp) + */ + + hashTarget = aHashTarget; + strTarget = hashTarget.GetHex(); + current_work.local_diff = strTarget; + + if (instance_of_cstratumparams.fstdErrDebugOutput) { + std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << + strprintf(" target = %s, komodo_diff = %g, ccminer_diff = %g", + strTarget, GetDifficultyFromBits(hashTarget.GetCompact(false)), ccminer::equi_stratum_target_to_diff(strTarget)) << std::endl; + } + } + + set_target_params.push_back(UniValue(strTarget)); // TODO: send real local diff (!) + set_target.push_back(Pair("params", set_target_params)); + + CMutableTransaction cb, bf; + std::vector cb_branch; + + // if (instance_of_cstratumparams.fstdErrDebugOutput) + // { + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " [1] cb = " << CTransaction(cb).ToString() << std::endl; + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " [1] current_work.GetBlock().vtx[0] = " << current_work.GetBlock().vtx[0].ToString() << std::endl; + // } + + { + std::vector extranonce1 = client.ExtraNonce1(job_id); + + static const std::vector dummy(32-extranonce1.size(), 0x00); // extranonce2 + CustomizeWork(client, current_work, client.m_addr, extranonce1, dummy, cb, bf, cb_branch); + + // without 2 lines below equihash solutinon on SubmitWork will be incorrect, bcz we should + // change vtx[0] in current work and re-calc hashMerkleRoot + // TODO: refactor all of these ... may be change this in current_work directly is bad idea, + // and we should do all checks and hashMerkleRoot at SubmitBlock(...) + + current_work.GetBlock().vtx[0] = cb; + current_work.GetBlock().hashMerkleRoot = current_work.GetBlock().BuildMerkleTree(); + + } + + // if (instance_of_cstratumparams.fstdErrDebugOutput) + // { + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " [2] cb = " << CTransaction(cb).ToString() << std::endl; + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " [2] current_work.GetBlock().vtx[0] = " << current_work.GetBlock().vtx[0].ToString() << std::endl; + // } + + CBlockHeader blkhdr; + // Setup native proof-of-work + + blkhdr = current_work.GetBlock().GetBlockHeader(); // copy entire blockheader created with CreateNewBlock to blkhdr + // CDataStream ds(SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); + CDataStream ds(SER_GETHASH, PROTOCOL_VERSION); + ds << cb; + + /* + HexInt4(blkhdr.nVersion) = 00000004, so we can't use it here, will use swab conversion via 1 of 3 methods: + + (1) params.push_back(HexStr((unsigned char *)&blkhdr.nVersion, (unsigned char *)&blkhdr.nVersion + sizeof(blkhdr.nVersion))); // VERSION + (2) std::vector vnVersion(4, 0); + WriteLE64(&vnVersion[0], blkhdr.nVersion); + params.push_back(HexStr(vnVersion)); + (3) params.push_back(HexInt4(bswap_32(blkhdr.nVersion))); + + Bytes order, cheatsheet: + + [ need ] fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e - HexStr(ToByteVector(blkhdr.hashFinalSaplingRoot)) + [ need ] fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e - HexStr(blkhdr.hashFinalSaplingRoot) + [ ---- ] 3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb - blkhdr.hashFinalSaplingRoot.GetHex() + [ ---- ] 3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb - blkhdr.hashFinalSaplingRoot.ToString() + */ + + /* mining.notify params */ + UniValue params(UniValue::VARR); // mining.notify params + params.push_back(HexStr(job_id.begin(), job_id.end())); // JOB_ID + params.push_back(HexInt4(bswap_32(blkhdr.nVersion))); // VERSION (0x4 -> "04000000") + params.push_back(HexStr(blkhdr.hashPrevBlock)); // PREVHASH + params.push_back(HexStr(blkhdr.hashMerkleRoot)); // MERKLEROOT + params.push_back(HexStr(blkhdr.hashFinalSaplingRoot)); // RESERVED -> hashFinalSaplingRoot + + UpdateTime(&blkhdr, Params().GetConsensus(), tip /* or pindexPrev [tip-1] is needed? */); + // blkhdr.nTime = GetTime(); + + params.push_back(HexInt4(bswap_32(blkhdr.nTime))); // TIME + params.push_back(HexInt4(bswap_32(blkhdr.nBits))); // BITS + + // Clean Jobs. If true, miners should abort their current work and immediately use the new job. + // If false, they can still use the current job, but should move to the new one after exhausting the current nonce range. + + UniValue clean_jobs(UniValue::VBOOL); + clean_jobs = client.m_last_tip != tip; + params.push_back(clean_jobs); // CLEAN_JOBS + + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << "New job: " << HexStr(job_id) << " " << strprintf("%08x", blkhdr.nTime) << std::endl; + + client.m_last_tip = tip; + + UniValue mining_notify(UniValue::VOBJ); + mining_notify.push_back(Pair("id", client.m_nextid++)); + mining_notify.push_back(Pair("method", "mining.notify")); + mining_notify.push_back(Pair("params", params)); + + return GetExtraNonceRequest(client, job_id) + + set_target.write() + "\n" + + mining_notify.write() + "\n"; +} +bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork& current_work, + const std::vector& extranonce1, const std::vector& extranonce2, + boost::optional nVersion, uint32_t nTime, const std::vector& sol) +{ + // called from stratum_mining_submit and uses following data, came from client: + // ["WORKER_NAME", "JOB_ID", "TIME", "NONCE_2", "EQUIHASH_SOLUTION"] + // all other params we have saved in other places + + if (extranonce1.size() + extranonce2.size() != 32) { + std::string msg = strprintf("extranonce1 [%d] length + extranonce2 [%d] length != %d", extranonce1.size(), extranonce2.size(), 32); + LogPrint("stratum", "%s\n", msg); + throw JSONRPCError(RPC_INVALID_PARAMETER, msg); + } + + // TODO: change hardcoded constants on actual determine of solution size, depends on equihash algo type: 200.9, etc. + if (sol.size() != 1347) { + std::string msg = strprintf("%s: solution is wrong length (received %d bytes; expected %d bytes", __func__, extranonce2.size(), 1347); + LogPrint("stratum", "%s\n", msg); + throw JSONRPCError(RPC_INVALID_PARAMETER, msg); + } + + CMutableTransaction cb, bf; + std::vector cb_branch; + CustomizeWork(client, current_work, client.m_addr, extranonce1, extranonce2, cb, bf, cb_branch); + + bool res = false; + { + // Check native proof-of-work + uint32_t version = current_work.GetBlock().nVersion; + + if (nVersion) version = *nVersion; + CBlockHeader blkhdr(current_work.GetBlock()); + + blkhdr.nVersion = version; + blkhdr.hashPrevBlock = current_work.GetBlock().hashPrevBlock; + // blkhdr.hashMerkleRoot = ComputeMerkleRootFromBranch(cb.GetHash(), cb_branch, 0); + // blkhdr.hashMerkleRoot = blkhdr.BuildMerkleTree(); + blkhdr.nTime = nTime; + blkhdr.nBits = current_work.GetBlock().nBits; + + // just an example of how-to reverse the things, don't needed in real life + // std::vector noncerev(extranonce1); + // std::reverse(noncerev.begin(), noncerev.end()); + // noncerev.insert(noncerev.begin(), extranonce2.rbegin(), extranonce2.rend()); + + std::vector nonce(extranonce1); + nonce.insert(nonce.end(), extranonce2.begin(), extranonce2.end()); + + blkhdr.nSolution = std::vector(sol.begin() + 3, sol.end()); + + blkhdr.hashFinalSaplingRoot = current_work.GetBlock().hashFinalSaplingRoot; + blkhdr.hashMerkleRoot = current_work.GetBlock().hashMerkleRoot; + blkhdr.nNonce = (uint256) nonce; + + // example how to display constructed block + // if (instance_of_cstratumparams.fstdErrDebugOutput) { + // CBlockIndex index {blkhdr}; + // index.SetHeight(current_work.nHeight); + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " blkhdr.hashPrevBlock = " << blkhdr.hashPrevBlock.GetHex() << std::endl; + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " blkhdr = " << blockToJSON(blkhdr, &index).write() << std::endl; + // } + + // block is constructed, now it's time to VerifyEH + + if (instance_of_cstratumparams.fCheckEquihashSolution && !CheckEquihashSolution(&blkhdr, Params())) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid equihash solution"); + + arith_uint256 bnTarget; bool fNegative, fOverflow; + bnTarget.SetCompact(blkhdr.nBits, &fNegative, &fOverflow); + + // check range + // if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit)) + // return false; + + if (UintToArith256(blkhdr.GetHash()) > bnTarget) { + res = false; + } else { + uint8_t pubkey33[33]; int32_t height = current_work.nHeight; + res = CheckProofOfWork(blkhdr, pubkey33, height, Params().GetConsensus()); + } + // if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << "res[1] = " << res << std::endl; + + uint256 hash = blkhdr.GetHash(); + + // bits = GetNextWorkRequired(blockindex, nullptr, Params().GetConsensus()); + + static uint64_t counter_TotalBlocks, counter_TotalShares, counter_prev; + if (res) + counter_TotalBlocks++; + counter_TotalShares++; + + // https://en.cppreference.com/w/cpp/types/integer - PRId64, PRIu64 (people always forget about various OS and format specifications) + + // native proof-of-work difficulty + CBlockIndex tmp_index; + + tmp_index.nBits = blkhdr.nBits; + double kmd_target_diff = GetDifficulty(&tmp_index); // diff from nbits (target) + tmp_index.nBits = UintToArith256(hash).GetCompact(); + double kmd_real_diff = GetDifficulty(&tmp_index); // real diff (from hash) + tmp_index.nBits = arith_uint256(current_work.local_diff).GetCompact(); + double kmd_local_diff = GetDifficulty(&tmp_index); // local diff (from local port diff) + + double ccminer_real_diff = ccminer::equi_stratum_target_to_diff(hash.ToString()); + double ccminer_target_diff = ccminer::equi_stratum_target_to_diff(arith_uint256().SetCompact(blkhdr.nBits).ToString()); + double ccminer_local_diff = ccminer::equi_stratum_target_to_diff(current_work.local_diff); + + static std::chrono::high_resolution_clock::time_point start; + std::chrono::high_resolution_clock::time_point finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed; + uint64_t shares_accepted_since_last; + + // TODO: we need to check hash > local port diff, and if it's true -> throw an exception -> diff too low (!) + if (!instance_of_cstratumparams.fAllowLowDiffShares) + if (UintToArith256(blkhdr.GetHash()) > arith_uint256(current_work.local_diff)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Low diff share (diff %g, local %g)", kmd_real_diff, kmd_local_diff)); + + if (finish > start) + { + elapsed = finish - start; + shares_accepted_since_last = counter_TotalShares - counter_prev; + start = finish; + counter_prev = counter_TotalShares; + // std::cerr << strprintf("%f ms - %" PRIu64 "", elapsed.count(), shares_accepted_since_last) << std::endl; + } + + bool fDisplayDiffKMD = true; // otherwise it will display ccminer diff + + std::cerr << DateTimeStrPrecise() << + strprintf("%saccepted: %" PRIu64 "/%" PRIu64 "%s ", ColorTypeNames[cl_WHT], counter_TotalBlocks, counter_TotalShares, ColorTypeNames[cl_N] ); + if (fDisplayDiffKMD) { + /* komodod diff display */ + std::cerr << strprintf("%slocal %g%s ", "\x1B[90m", kmd_local_diff, ColorTypeNames[cl_N]) << + strprintf("%s(diff %g, target %g) %s ", ColorTypeNames[cl_WHT], kmd_real_diff, kmd_target_diff, ColorTypeNames[cl_N]); + } + else + { /* ccminer diff display */ + std::cerr << strprintf("%slocal %.3f%s ", "\x1B[90m", ccminer_local_diff, ColorTypeNames[cl_N]) << + strprintf("%s(diff %.3f, target %.3f) %s", ColorTypeNames[cl_WHT], ccminer_real_diff, ccminer_target_diff, ColorTypeNames[cl_N]); // ccminer diff + } + + std::cerr << "" << + strprintf("%f ms ", elapsed.count()) << // 1 share took elapsed ms + strprintf("%s%s%s ", ColorTypeNames[cl_LGR], (res ? "yay!!!": "yes!"), ColorTypeNames[cl_N]) << + std::endl; + + // (diff %g, target %g), % + if (res) { + + LogPrintf("GOT BLOCK!!! by %s: %s\n", client.m_addr.ToString(), hash.ToString()); + + CBlock block(current_work.GetBlock()); + // block.vtx[0] = MakeTransactionRef(std::move(cb)); + block.vtx[0] = cb; + + // if (!current_work.m_aux_hash2 && current_work.m_is_witness_enabled) { + // block.vtx.back() = MakeTransactionRef(std::move(bf)); + // } + block.nVersion = version; + // block.hashMerkleRoot = BlockMerkleRoot(block); + block.hashMerkleRoot = block.BuildMerkleTree(); + //if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << "hashMerkleRoot = " << block.hashMerkleRoot.GetHex() << std::endl; + + block.nTime = nTime; + // block.nNonce = nNonce; + // nNonce <<= 32; nNonce >>= 16; // clear the top and bottom 16 bits (for local use as thread flags and counters) + + block.nNonce = (uint256) nonce; + block.nSolution = std::vector(sol.begin() + 3, sol.end()); + + // example how to pre-check the equihash solution + // if(instance_of_cstratumparams.fstdErrDebugOutput) { + // CBlockIndex index {blkhdr}; + // index.SetHeight(-1); + // std::cerr << "block = " << blockToJSON(block, &index, true).write(1) << std::endl; + // std::cerr << "CheckEquihashSolution = " << CheckEquihashSolution(&block, Params()) << std::endl; + // } + + // std::shared_ptr pblock = std::make_shared(block); + // res = ProcessNewBlock(Params(), pblock, true, NULL); + + CValidationState state; + res = ProcessNewBlock(0,0,state, NULL, &block, true /* forceProcessing */ , NULL); + + //if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << "res[2] = " << res << std::endl; + + if (res) { + // LOCK(cs_main); + if (!mapBlockIndex.count(hash)) { + LogPrintf("Unable to find new block index entry; cannot prioritise block 0x%s\n", hash.ToString()); + } else + { + CBlockIndex* block_index = mapBlockIndex.at(hash); + CValidationState state; + // we haven't PreciousBlock, so we can't prioritize the block this way for now + + // PreciousBlock(state, Params(), block_index); + // if (!state.IsValid()) { + // LogPrintf("Database error while prioritising new block 0x%s: %d (%s) %s\n", hash.ToString(), state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage()); + // } + } + } + } else { + LogPrintf("NEW SHARE!!! by %s: %s\n", client.m_addr.ToString(), hash.ToString()); + } + } + + if (res) { + client.m_send_work = true; + } + + return res; +} + +void BoundParams(const std::string& method, const UniValue& params, size_t min, size_t max) +{ + if (params.size() < min) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s expects at least %d parameters; received %d", method, min, params.size())); + } + + if (params.size() > max) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s receives no more than %d parameters; got %d", method, max, params.size())); + } +} + +UniValue stratum_mining_subscribe(StratumClient& client, const UniValue& params) +{ + const std::string method("mining.subscribe"); + BoundParams(method, params, 0, 4); + + if (params.size() >= 1) { + client.m_client = params[0].get_str(); + LogPrint("stratum", "Received subscription from client %s\n", client.m_client); + } + + // According to 'Stratum protocol changes for ZCash' - https://github.com/slushpool/poclbm-zcash/wiki/Stratum-protocol-changes-for-ZCash + // mining.subscribe params looks like following: + + // {"id": 1, "method": "mining.subscribe", "params": ["CONNECT_HOST", CONNECT_PORT, "MINER_USER_AGENT", "SESSION_ID"]} + // So, params[params.size()-1] should be SESSION_ID, but currently we don't support it. + + // Also we should answer with these: + // {"id": 1, "result": ["SESSION_ID", "NONCE_1"], "error": null} + // {"id":1,"result":[null,"81000001"],"error":null} + + // NONCE_1 is first part of the block header nonce (in hex). + + // By protocol, Zcash's nonce is 32 bytes long. The miner will pick NONCE_2 such that len(NONCE_2) = 32 - len(NONCE_1). + // Please note that Stratum use hex encoding, so you have to convert NONCE_1 from hex to binary before. + + // ["CONNECT_HOST", CONNECT_PORT, "MINER_USER_AGENT", "SESSION_ID"] + // ["NiceHash/1.0.0", null, "stratum3.decker.host", 18776] // ua, session_id, host, port? + // ["ccminer/2.3.1"] + + UniValue ret(UniValue::VARR); + + // ExtraNonce1 -> client.m_supports_extranonce is false, so the job_id isn't used + std::vector vExtraNonce1 = client.ExtraNonce1(uint256()); + + // std::string sExtraNonce1 = HexStr(vExtraNonce1.begin(), vExtraNonce1.begin() + (vExtraNonce1.size() > 3 ? 4 : vExtraNonce1.size())); + std::string sExtraNonce1 = HexStr(vExtraNonce1); + + /** + * Potentially we can use something like strprintf("%08x", GetRand(std::numeric_limits::max()) + * here to generate sExtraNonce1, but don't forget that client.ExtraNonce1 method return 8 bytes + * job_nonce:8 = sha256(client.m_secret:32 + client.job_id:32) , so, somewhere in future we can re-calculate + * sExtraNonce1 for a given client based on m_secret. + */ + + // if (instance_of_cstratumparams.fstdErrDebugOutput && vExtraNonce1.size() > 3) { + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " " << strprintf("client.m_supports_extranonce = %d, [%d, %d, %d, %d], %s", client.m_supports_extranonce, vExtraNonce1[0], vExtraNonce1[1], vExtraNonce1[2], vExtraNonce1[3], sExtraNonce1) << std::endl; + // // recalc from client.m_secret example + // uint256 sha256; + // CSHA256().Write(client.m_secret.begin(), 32).Finalize(sha256.begin()); + // std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << " " << HexStr(std::vector(sha256.begin(), sha256.begin() + 4)) << std::endl; + // } + + ret.push_back(NullUniValue); + ret.push_back(sExtraNonce1); + + // On mining.subscribe we don't need ti send anything else, we will send + // mining.set_target and mining.notify bit later, inside GetWorkUnit. + // Scheme is the following: + // 1. stratum_read_cb(bufferevent * bev, void * ctx) + // 2. if (client.m_send_work) -> GetWorkUnit + // 3. CustomizeWork (throw if error and exit from GetWorkUnit) + // 4. set_target + // 5. ... + // Last. GetWorkUnit returns string data (!) to send to client ( ... + mining.set_target + mining.notify + ... ) + + return ret; +} + +UniValue stratum_mining_authorize(StratumClient& client, const UniValue& params) +{ + const std::string method("mining.authorize"); + BoundParams(method, params, 1, 2); + + std::string username = params[0].get_str(); + boost::trim(username); + + // params[1] is the client-provided password. We do not perform + // user authorization, so we ignore this value. + + double mindiff = 0.0; + size_t pos = username.find('+'); + if (pos != std::string::npos) { + // Extract the suffix and trim it + std::string suffix(username, pos+1); + boost::trim_left(suffix); + // Extract the minimum difficulty request + mindiff = boost::lexical_cast(suffix); + // Remove the '+' and everything after + username.resize(pos); + boost::trim_right(username); + } + + CBitcoinAddress addr(get_stripped_username(username)); + + if (!addr.IsValid()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid Komodo address: %s", username)); + } + + client.m_addr = addr; + client.m_mindiff = mindiff; + client.m_authorized = true; + + client.m_send_work = true; + + LogPrintf("Authorized stratum miner %s from %s, mindiff=%f\n", addr.ToString(), client.GetPeer().ToString(), mindiff); + + return true; +} + +UniValue stratum_mining_configure(StratumClient& client, const UniValue& params) +{ + const std::string method("mining.configure"); + BoundParams(method, params, 2, 2); + + UniValue res(UniValue::VOBJ); + + UniValue extensions = params[0].get_array(); + UniValue config = params[1].get_obj(); + for (int i = 0; i < extensions.size(); ++i) { + std::string name = extensions[i].get_str(); + + if ("version-rolling" == name) { + uint32_t mask = ParseHexInt4(find_value(config, "version-rolling.mask"), "version-rolling.mask"); + size_t min_bit_count = find_value(config, "version-rolling.min-bit-count").get_int(); + client.m_version_rolling_mask = mask & 0x1fffe000; + res.push_back(Pair("version-rolling", true)); + res.push_back(Pair("version-rolling.mask", HexInt4(client.m_version_rolling_mask))); + LogPrint("stratum", "Received version rolling request from %s\n", client.GetPeer().ToString()); + } + + else { + LogPrint("stratum", "Unrecognized stratum extension '%s' sent by %s\n", name, client.GetPeer().ToString()); + } + } + + return res; +} + +UniValue stratum_mining_submit(StratumClient& client, const UniValue& params) +{ + // {"id": 4, "method": "mining.submit", "params": ["WORKER_NAME", "JOB_ID", "TIME", "NONCE_2", "EQUIHASH_SOLUTION"]}\n + + // NONCE_1 is first part of the block header nonce (in hex). + // By protocol, Zcash's nonce is 32 bytes long. The miner will pick NONCE_2 such that len(NONCE_2) = 32 - len(NONCE_1). Please note that Stratum use hex encoding, so you have to convert NONCE_1 from hex to binary before. + // NONCE_2 is the second part of the block header nonce. + /** + * + * {"method":"mining.submit","params":[ + [0] WORKER_NAME "RDeckerSubnU8QVgrhj27apzUvbVK3pnTk", + [1] JOB_ID "1", + [2] TIME "0eaec560", + [3] NONCE_2 "0000000000000000d890000001000000000000000000000000000000", + [4] EQUIHASH_SOLUTION(1347) "fd400501762fe7c0d228a4b249727f52d85c3d5d989b3f9d07148506820a50e6db2ba3456b4ccfb168d7eb65651c7d7b893d87fb77077b56224a6fc9b9ca283b7a44a25be67d956ee55f9aaeca80eae765076495fd2eb50cf3e279a68dfd15ae6b30e911db6331d6717f352510b5834d3045db3833cdf74d1fe8379ab7b4fe46fe0d855c964085d5779701a25dbcd601ea87fb5d4bbe16c39e9c5fa22c874b4922605ed21411353cef39ce02b954a09961742d8011060a3c45f6b5b316d4a1d75530bd45722945d7a8d4698e75f49b86a485b7f1851b47d10d66d74eebb492c4269d34ca3691a459a80427f79f6d01e469bb250715fc49420d6e87383b598804bdf8b50b8510e44fd0740aa5650ed5ba19543c8657f67b5164d610bbb0ab75da1c48e81e9a8a9861bc119a31c695c5c3530ae271cf9ab4a2fa08d2b4fc851e273c324dc926d6901ca20ba5fed13118f12925760871909e8351d9e944c2959a61bf74238a587dd32826de63ab4819473bb3fad67c9a54baeddd137cb6350a25969531fa055dee51464b36cbdfb6afc4be0cee0f0fe11188c8d70d0238b3ba0c6459cd34d8b7bd8b1cdaa2b7728d51269707a70c54faac778eb4bcb6492e5fcc32406ed87fdfaecc52c9f461af3f4c3c51b529e2ab9a0e15a15b3cdcb35fe3bfe4854952ae975e3171cd2600a54509d386d45ecf668b5a17249b157a13212d0e465bc1796048d63c7b4027cb0850b9607261800e4fe6217e1fba2a28601aec9b524dac787a6c14df668a7c4fabf51f8885be7ed84ca72d0ff9a7491fddae1f5309441d243cb6d5c5c4f45a08b1b858bd15ef4d1ca1565c39000f9298b52e4221723457a0ec2e904aa6cd96e854cd8c1bbd07f1c9237c831d694817227aafe7873c43826e691d3971e82e87b538c42a48603696075b19c72b85c7d20863635621da1939d9024a434f6d840cac7a30058a51650485eabf9c0735163fd9b468249ea5889c62b4f739f58665d7f8c5010a661c1355ea7e9d85b6a18424b0027e86df5aa42b1bc2bb7a38b69c8db620251c4138b69956235640c502e26185d923a045777919984e71558edb77fb54981c6ac3dc979cd0b4f704874f02536daae894da78f31913554f91a30d6badc935fe58cc9d29d152138dde520ddb9906966e077ee3380641ce88fa74a658245202a8183e1807100c3f7d22df6577f309e4d85429e94a6f6f5dbaec3653ca6414bf6ed8794db84b7860be1984cb525b235cdb263cd527c74aa6d336615e1d361f4965ddad1fd191bc4a72fa92acc13a7c92b6e0ee077d70911004f422813e408a49ba38b950ead458b72cacb1ede9e35e2fd002eaaad0ecc2cf62801e4fe010a2cfd7190c51337513f1819acf170dda5f3b23452f36d28c20509a39fedd658f45c5e58a02feb64b0e027e05804350afc3220e53fe1761e93d018f3be9eb3554ecc98fe9fdc584ac06c0dcd63812180e94876f42f2955e242358d590a8b521b641b9729e6c7dcf6164571758ac2b2ee7656f0b0e986abf7f6b569daca304c944ded083ff202a80e8636fe9aeae39707401b321a6094c4a59cc7bcec9852189c746697963f7062304d57335795ec60dd49081a4329d3b1a8c9d55f67d11f36fb54133e67fe8a362a1f8db601aa054d97d3002f898374fd201f10af65393c9c3634e0139551e362da976b7aa0f4f8156aef59620bd24a216663784d205ef5976aa3cf6a9eed571de7cb350a355c35b67c621184608f72357d32d49842e5534f232567ed7ef9a0edc109b3b487e86d1cdd9231969a76e5d7c54bc3e28942e99301a89c13895c2bc5acac2111f53182951183f50c839601dc5fabfd39d95258c79b93a140ab727288179ce1262b13e8cc5a829edf26e7d241fbf6b" + ], + "id":10} + */ + + const std::string method("mining.submit"); + BoundParams(method, params, 5,5); + // First parameter is the client username, which is ignored. + + /* EWBF 31 bytes job_id fix */ + bool fEWBFJobIDFixNeeded = false; + uint256 ret; + if (params[1].isStr()) { + //std::cerr << "\"" << params[1].get_str() << "\"" << std::endl; + const std::string job_id_str = params[1].get_str(); + const std::string hexDigits = "0123456789abcdef"; + // std::cerr << strprintf("\"%s\" (%d)", job_id_str, job_id_str.length()) << std::endl; + if (job_id_str.length() == 63) { + fEWBFJobIDFixNeeded = true; + for(const auto& hexDigit : hexDigits) { + ret = uint256(ParseHex(job_id_str + hexDigit)); + if (work_templates.count(ret)) break; + } + } + } + + uint256 job_id; + if (!fEWBFJobIDFixNeeded) + job_id = ParseUInt256(params[1], "job_id"); + else + job_id = ret; + + // uint256 job_id = ParseUInt256(params[1], "job_id"); + if (!work_templates.count(job_id)) { + LogPrint("stratum", "Received completed share for unknown job_id : %s\n", HexStr(job_id.begin(), job_id.end())); + return false; + } + + StratumWork ¤t_work = work_templates[job_id]; + + uint32_t nTime = bswap_32(ParseHexInt4(params[2], "nTime")); + + std::vector sol = ParseHexV(params[4], "solution"); + if (sol.size() != 1347) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("solution is wrong length (received %d bytes; expected %d bytes", sol.size(), 1347)); + } + + std::vector extranonce1 = client.ExtraNonce1(job_id); + std::vector extranonce2 = ParseHexV(params[3], "extranonce2"); + boost::optional nVersion = 4; // block version always 4 + + SubmitBlock(client, job_id, current_work, extranonce1, extranonce2, nVersion, nTime, sol); + + return true; +} + +UniValue stratum_mining_extranonce_subscribe(StratumClient& client, const UniValue& params) +{ + const std::string method("mining.extranonce.subscribe"); + BoundParams(method, params, 0, 0); + + /*client.m_supports_extranonce = true; + + return true;*/ + client.m_supports_extranonce = false; + + return true; +} + +UniValue stratum_mining_multi_version(StratumClient& client, const UniValue& params) +{ + const std::string method("mining.multi_version"); + BoundParams(method, params, 0, 1); + + /* + Received stratum request from Miner: {"id": 135828, "method": "mining.multi_version", "params": [1]} + Sending stratum response to Miner : {"result":null,"error":{"code":-32601,"message":"Method 'mining.multi_version' not found"},"id":135828} + */ + + return false; +} + + + +/** Callback to write from a stratum connection. */ +static void stratum_write_cb(bufferevent *bev, void *ctx) +{ + /* template */ +} + +/** Callback to read from a stratum connection. */ +static void stratum_read_cb(bufferevent *bev, void *ctx) +{ + evconnlistener *listener = (evconnlistener*)ctx; + LOCK(cs_stratum); + // Lookup the client record for this connection + if (!subscriptions.count(bev)) { + LogPrint("stratum", "Received read notification for unknown stratum connection 0x%x\n", (size_t)bev); + return; + } + StratumClient& client = subscriptions[bev]; + // Get links to the input and output buffers + evbuffer *input = bufferevent_get_input(bev); + assert(input); + evbuffer *output = bufferevent_get_output(bev); + assert(output); + + // Process each line of input that we have received + char *cstr = 0; + size_t len = 0; + while ((cstr = evbuffer_readln(input, &len, EVBUFFER_EOL_CRLF))) { + std::string line(cstr, len); + free(cstr); + LogPrint("stratum", "Received stratum request from %s : %s\n", client.GetPeer().ToString(), line); + + //JSONRPCRequest jreq; + JSONRequest jreq; + + std::string reply; + try { + // Parse request + UniValue valRequest; + if (!valRequest.read(line)) { + // Not JSON; is this even a stratum miner? + throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + } + if (!valRequest.isObject()) { + // Not a JSON object; don't know what to do. + throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); + } + if (valRequest.exists("result")) { + // JSON-RPC reply. Ignore. + LogPrint("stratum", "Ignoring JSON-RPC response\n"); + continue; + } + jreq.parse(valRequest); + + // Dispatch to method handler + UniValue result = NullUniValue; + if (stratum_method_dispatch.count(jreq.strMethod)) { + result = stratum_method_dispatch[jreq.strMethod](client, jreq.params); + } else { + throw JSONRPCError(RPC_METHOD_NOT_FOUND, strprintf("Method '%s' not found", jreq.strMethod)); + } + + // Compose reply + reply = JSONRPCReply(result, NullUniValue, jreq.id); + } catch (const UniValue& objError) { + reply = JSONRPCReply(NullUniValue, objError, jreq.id); + } catch (const std::exception& e) { + reply = JSONRPCReply(NullUniValue, JSONRPCError(RPC_INTERNAL_ERROR, e.what()), jreq.id); + } + + LogPrint("stratum", "Sending stratum response to %s : %s", client.GetPeer().ToString(), reply); + assert(output); + if (evbuffer_add(output, reply.data(), reply.size())) { + LogPrint("stratum", "Sending stratum response failed. (Reason: %d, '%s')\n", errno, evutil_socket_error_to_string(errno)); + } + } + + // If required, send new work to the client. + if (client.m_send_work) { + std::string data; + try { + data = GetWorkUnit(client); + } catch (const UniValue& objError) { + data = JSONRPCReply(NullUniValue, objError, NullUniValue); + } catch (const std::exception& e) { + data = JSONRPCReply(NullUniValue, JSONRPCError(RPC_INTERNAL_ERROR, e.what()), NullUniValue); + } + + LogPrint("stratum", "Sending requested stratum work unit to %s : %s", client.GetPeer().ToString(), data); + assert(output); + if (evbuffer_add(output, data.data(), data.size())) { + LogPrint("stratum", "Sending stratum work unit failed. (Reason: %d, '%s')\n", errno, evutil_socket_error_to_string(errno)); + } + + client.m_send_work = false; + } +} + +/** Callback to handle unrecoverable errors in a stratum link. */ +static void stratum_event_cb(bufferevent *bev, short what, void *ctx) +{ + evconnlistener *listener = (evconnlistener*)ctx; + LOCK(cs_stratum); + // Fetch the return address for this connection, for the debug log. + std::string from("UNKNOWN"); + if (!subscriptions.count(bev)) { + LogPrint("stratum", "Received event notification for unknown stratum connection 0x%x\n", (size_t)bev); + return; + } else { + from = subscriptions[bev].GetPeer().ToString(); + } + // Report the reason why we are closing the connection. + if (what & BEV_EVENT_ERROR) { + LogPrint("stratum", "Error detected on stratum connection from %s\n", from); + } + if (what & BEV_EVENT_EOF) { + LogPrint("stratum", "Remote disconnect received on stratum connection from %s\n", from); + } + // Remove the connection from our records, and tell libevent to + // disconnect and free its resources. + if (what & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { + LogPrint("stratum", "Closing stratum connection from %s\n", from); + subscriptions.erase(bev); + if (bev) { + bufferevent_free(bev); + bev = NULL; + } + } +} + +/** Callback to accept a stratum connection. */ +static void stratum_accept_conn_cb(evconnlistener *listener, evutil_socket_t fd, sockaddr *address, int socklen, void *ctx) +{ + // Parse the return address + CService from; + from.SetSockAddr(address); + // Early address-based allow check + + if (!ClientAllowed(stratum_allow_subnets, from)) + { + // evconnlistener_free(listener); + + /* + Here we shouldn't free listener, bcz if somebody will connect on stratum port from + disallowed network -> future connections will be anavailable. + */ + LogPrint("stratum", "Rejected connection from disallowed subnet: %s\n", from.ToString()); + return; + } + + { + LOCK(cs_stratum); + // Should be the same as EventBase(), but let's get it the official way. + event_base *base = evconnlistener_get_base(listener); + // Create a buffer for sending/receiving from this connection. + bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); + // Disable Nagle's algorithm, so that TCP packets are sent + // immediately, even if it results in a small packet. + int one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)); + // Setup the read and event callbacks to handle receiving requests + // from the miner and error handling. A write callback isn't + // needed because we're not sending enough data to fill buffers. + bufferevent_setcb(bev, stratum_read_cb, NULL, stratum_event_cb, (void*)listener); + // Enable bidirectional communication on the connection. + bufferevent_enable(bev, EV_READ|EV_WRITE); + // Record the connection state + subscriptions[bev] = StratumClient(listener, fd, bev, from); + // Log the connection. + LogPrint("stratum", "Accepted stratum connection from %s\n", from.ToString()); + } +} + +/** Setup the stratum connection listening services */ +static bool StratumBindAddresses(event_base* base) +{ + int stratumPort = BaseParams().StratumPort(); + int defaultPort = GetArg("-stratumport", stratumPort); + std::vector > endpoints; + + // Determine what addresses to bind to + if (!mapArgs.count("-stratumallowip")) { // Default to loopback if not allowing external IPs + endpoints.push_back(std::make_pair("::1", defaultPort)); + endpoints.push_back(std::make_pair("127.0.0.1", defaultPort)); + if (mapArgs.count("-stratumbind")) { + LogPrintf("WARNING: option -stratumbind was ignored because -stratumallowip was not specified, refusing to allow everyone to connect\n"); + } + } else if (mapArgs.count("-stratumbind")) { // Specific bind address + const std::vector& vbind = mapMultiArgs["-stratumbind"]; + for (std::vector::const_iterator i = vbind.begin(); i != vbind.end(); ++i) { + int port = defaultPort; + std::string host; + SplitHostPort(*i, port, host); + endpoints.push_back(std::make_pair(host, port)); + } + } else { // No specific bind address specified, bind to any + endpoints.push_back(std::make_pair("::", defaultPort)); + endpoints.push_back(std::make_pair("0.0.0.0", defaultPort)); + } + + // Bind each addresses + for (const auto& endpoint : endpoints) { + LogPrint("stratum", "Binding stratum on address %s port %i\n", endpoint.first, endpoint.second); + // Use CService to translate string -> sockaddr + CNetAddr netaddr; + std::vector vIP; + + LookupHost(endpoint.first.c_str(), vIP, 1, true); + assert(vIP.size() >= 1); + + netaddr = vIP[0]; + + CService socket(netaddr, endpoint.second); + union { + sockaddr ipv4; + sockaddr_in6 ipv6; + } addr; + socklen_t len = sizeof(addr); + socket.GetSockAddr((sockaddr*)&addr, &len); + // Setup an event listener for the endpoint + evconnlistener *listener = evconnlistener_new_bind(base, stratum_accept_conn_cb, NULL, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, (sockaddr*)&addr, len); + // Only record successful binds + if (listener) { + bound_listeners[listener] = socket; + } else { + LogPrintf("Binding stratum on address %s port %i failed. (Reason: %d, '%s')\n", endpoint.first, endpoint.second, errno, evutil_socket_error_to_string(errno)); + } + } + + return !bound_listeners.empty(); +} + +/** Watches for new blocks and send updated work to miners. */ +static bool g_shutdown = false; + +void BlockWatcher() +{ + RenameThread("blkwatcher"); + boost::unique_lock lock(csBestBlock); + boost::system_time checktxtime; + boost::system_time starttime = boost::get_system_time(); + + unsigned int txns_updated_last = 0; + boost::posix_time::time_duration time_passed = boost::posix_time::seconds(0); + bool fRebroadcastAnyway = false; + + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << " time = " << boost::get_system_time() << " checktxtime = " << checktxtime << std::endl; + while (true) { // (A) + /* This will execute before waiting of cvBlockChange */ + + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << " time = " << boost::get_system_time() << " checktxtime = " << checktxtime << std::endl; + checktxtime = boost::get_system_time() + boost::posix_time::seconds(txMemPoolCheckTimeout); + // - time_passed + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << " time = " << boost::get_system_time() << " checktxtime = " << checktxtime << std::endl; + + if (!cvBlockChange.timed_wait(lock, checktxtime)) { + // Timeout: Check to see if mempool was updated. + + /* This will execute after txMemPoolCheckTimeout seconds */ + unsigned int txns_updated_next = mempool.GetTransactionsUpdated(); + + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << ColorTypeNames[cl_WHT] << " seconds_passed = " << (boost::get_system_time() - starttime) << ColorTypeNames[cl_N] << " txns_updated_last = " << txns_updated_last << " txns_updated_next = " << txns_updated_next << std::endl; + time_passed = boost::posix_time::time_duration(boost::get_system_time() - starttime); + + if ((boost::get_system_time() - starttime) < boost::posix_time::seconds(jobRebroadcastTimeout)) { + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << "seconds_passed < jobRebroadcastTimeout" << std::endl; + fRebroadcastAnyway = false; + if (txns_updated_last == txns_updated_next) continue; // (A) + } else { + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << ColorTypeNames[cl_GRN] << "Force update work!"<< ColorTypeNames[cl_N] << " seconds_passed >= jobRebroadcastTimeout" << std::endl; + // in case of rebroadcast we should "emulate" that everything is changed and clients must go for new work + mempool.AddTransactionsUpdated(1); + for (auto& subscription : subscriptions) { + subscription.second.m_last_tip = (subscription.second.m_last_tip ? nullptr : chainActive.Tip()); + } + fRebroadcastAnyway = true; + starttime += boost::posix_time::seconds(jobRebroadcastTimeout); + } + + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << std::endl; + txns_updated_last = txns_updated_next; + } + + /* This will excute after wait cvBlockChange will completed, or if 'timeout branch' will allow + execution goes here (mean, if it will not use condition with `continue`) */ + + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << " time = " << boost::get_system_time() << " checktxtime = " << checktxtime << std::endl; + + if (g_shutdown) { + break; + } + + // Either new block, or updated transactions. Either way, + // send updated work to miners. + { + LOCK(cs_stratum); + for (auto& subscription : subscriptions) { + + bufferevent* bev = subscription.first; + + if (!bev) + continue; + evbuffer *output = bufferevent_get_output(bev); + if (!output) + continue; + + StratumClient& client = subscription.second; + // Ignore clients that aren't authorized yet. + if (!client.m_authorized && client.m_aux_addr.empty()) { + continue; + } + // Ignore clients that are already working on the new block. + // Typically this is just the miner that found the block, who was + // immediately sent a work update. This check avoids sending that + // work notification again, moments later. Due to race conditions + // there could be more than one miner that have already received an + // update, however. + if (!fRebroadcastAnyway && client.m_last_tip == chainActive.Tip()) { + continue; + } + + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << " time = " << boost::get_system_time() << " checktxtime = " << checktxtime << std::endl; + + // Get new work + std::string data; + try { + data = GetWorkUnit(client); + } catch (const UniValue& objError) { + data = JSONRPCReply(NullUniValue, objError, NullUniValue); + } catch (const std::exception& e) { + // Some sort of error. Ignore. + std::string msg = strprintf("Error generating updated work for stratum client: %s", e.what()); + LogPrint("stratum", "%s\n", msg); + data = JSONRPCReply(NullUniValue, JSONRPCError(RPC_INTERNAL_ERROR, msg), NullUniValue); + } + // Send the new work to the client + + assert(output); + if (evbuffer_add(output, data.data(), data.size())) { + LogPrint("stratum", "Sending stratum work unit failed. (Reason: %d, '%s')\n", errno, evutil_socket_error_to_string(errno)); + } + } + } + + if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << " time = " << boost::get_system_time() << " checktxtime = " << checktxtime << std::endl; + } +} + +void SendKeepAlivePackets() +{ + RenameThread("sockklv"); + while (true) { + // Run the notifier on an integer second in the steady clock. + auto now = std::chrono::steady_clock::now().time_since_epoch(); + auto nextFire = std::chrono::duration_cast( + now + std::chrono::seconds(10)); + std::this_thread::sleep_until( + std::chrono::time_point(nextFire)); + + boost::this_thread::interruption_point(); + + // Either new block, or updated transactions. Either way, + // send updated work to miners. + for (auto& subscription : subscriptions) { + bufferevent* bev = subscription.first; + + if (!bev) + continue; + evbuffer *output = bufferevent_get_output(bev); + if (!output) + continue; + evbuffer *input = bufferevent_get_input(bev); + if (!input) + continue; + + StratumClient& client = subscription.second; + + if (instance_of_cstratumparams.fstdErrDebugOutput) { + std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << std::endl << + "client.m_authorized = " << client.m_authorized << std::endl << + "client.m_aux_addr.size() = " << client.m_aux_addr.size() << std::endl << + "client.m_last_tip = " << strprintf("%p", client.m_last_tip) << std::endl << + (client.m_last_tip ? strprintf("client.m_last_tip->GetHeight() = %d", client.m_last_tip->GetHeight()) : "") << std::endl << + "chainActive.Tip()->GetHeight() = " << chainActive.Tip()->GetHeight() << std::endl << + "client.m_supports_extranonce = " << client.m_supports_extranonce << std::endl << + "client.m_send_work = " << client.m_send_work << std::endl << + std::endl; + } + + // Ignore clients that aren't authorized yet. + if (!client.m_authorized && client.m_aux_addr.empty()) { + continue; + } + + std::string data = "\r\n"; + // to see the socket / connection is alive, we will see bunch of + // JSON decode failed(1): '[' or '{' expected near end of file + // on client if will send "\r\n" every second + + assert(output); + if (evbuffer_add(output, data.data(), data.size())) { + LogPrint("stratum", "Sending stratum keepalive unit failed. (Reason: %d, '%s')\n", errno, evutil_socket_error_to_string(errno)); + } + + if ( (client.m_last_tip && client.m_last_tip->GetHeight() == chainActive.Tip()->GetHeight()) || (!client.m_last_tip) ) + { + LOCK(cs_stratum); + std::cerr << DateTimeStrPrecise() << "\033[31m" << client.m_from.ToString() << "\033[0m seems stucked (ccminer issue), need to emulate new block incoming to unstuck!" << std::endl; + mempool.AddTransactionsUpdated(1); + client.m_last_tip = (client.m_last_tip ? nullptr : chainActive.Tip()); + client.m_nextid++; + cvBlockChange.notify_all(); // change the state of all threads waiting on *this to ready + // cvBlockChange.notify_one(); // if there is a thread waiting on *this, change that thread state to ready + } + } + + } + +} + +/** Configure the Hush stratum server */ +bool InitStratumServer() +{ + LOCK(cs_stratum); + fprintf(stderr,"%s: Starting built-in stratum server\n",__func__); + + + if (!InitStratumAllowList(stratum_allow_subnets)) { + LogPrint("stratum", "Unable to bind stratum server to an endpoint.\n"); + return false; + } + + std::string strAllowed; + for(const CSubNet& subnet : stratum_allow_subnets) + strAllowed += subnet.ToString() + " "; + LogPrint("stratum", "Allowing Stratum connections from: %s\n", strAllowed); + + event_base* base = EventBase(); + if (!base) { + LogPrint("stratum", "No event_base object, cannot setup stratum server.\n"); + return false; + } + + if (!StratumBindAddresses(base)) { + LogPrintf("Unable to bind any endpoint for stratum server\n"); + } else { + LogPrint("stratum", "Initialized stratum server\n"); + } + + stratum_method_dispatch["mining.subscribe"] = stratum_mining_subscribe; + stratum_method_dispatch["mining.authorize"] = stratum_mining_authorize; + stratum_method_dispatch["mining.configure"] = stratum_mining_configure; + stratum_method_dispatch["mining.submit"] = stratum_mining_submit; + stratum_method_dispatch["mining.extranonce.subscribe"] = + stratum_mining_extranonce_subscribe; + stratum_method_dispatch["mining.multi_version"] = + stratum_mining_multi_version; + + + // Start thread to wait for block notifications and send updated + // work to miners. + block_watcher_thread = boost::thread(BlockWatcher); + // block_watcher_thread = boost::thread(SendKeepAlivePackets); + + return true; +} + +/** Interrupt the stratum server connections */ +void InterruptStratumServer() +{ + LOCK(cs_stratum); + // Stop listening for connections on stratum sockets + for (const auto& binding : bound_listeners) { + LogPrint("stratum", "Interrupting stratum service on %s\n", binding.second.ToString()); + evconnlistener_disable(binding.first); + } + // Tell the block watching thread to stop + g_shutdown = true; +} + +/** Cleanup stratum server network connections and free resources. */ +void StopStratumServer() +{ + LOCK(cs_stratum); + /* Tear-down active connections. */ + for (const auto& subscription : subscriptions) { + LogPrint("stratum", "Closing stratum server connection to %s due to process termination\n", subscription.second.GetPeer().ToString()); + bufferevent_free(subscription.first); + } + subscriptions.clear(); + /* Un-bind our listeners from their network interfaces. */ + for (const auto& binding : bound_listeners) { + LogPrint("stratum", "Removing stratum server binding on %s\n", binding.second.ToString()); + evconnlistener_free(binding.first); + } + bound_listeners.clear(); + /* Free any allocated block templates. */ + work_templates.clear(); +} + +/* RPC */ +UniValue rpc_stratum_updatework(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + if (fHelp || params.size() != 0) + throw std::runtime_error( + "stratum_updatework\n" + "Tries to immediatelly update work on all connected miners.\n" + "\nExamples:\n" + + HelpExampleCli("stratum_updatework", "") + + HelpExampleRpc("stratum_updatework", "") + ); + + UniValue obj(UniValue::VOBJ); + UniValue json_clients(UniValue::VARR); + uint64_t skipped = 0; + + // send updated work to miners + + // if (cs_stratum.try_lock()) + { + LOCK(cs_stratum); + for (auto& subscription : subscriptions) { + + bufferevent* bev = subscription.first; + + if (!bev) + continue; + evbuffer *output = bufferevent_get_output(bev); + if (!output) + continue; + evbuffer *input = bufferevent_get_input(bev); + if (!input) + continue; + + StratumClient& client = subscription.second; + + if (instance_of_cstratumparams.fstdErrDebugOutput) { + std::cerr << __func__ << ": " << __FILE__ << "," << __LINE__ << std::endl << + "client.m_authorized = " << client.m_authorized << std::endl << + "client.m_aux_addr.size() = " << client.m_aux_addr.size() << std::endl << + "client.m_last_tip = " << strprintf("%p", client.m_last_tip) << std::endl << + (client.m_last_tip ? strprintf("client.m_last_tip->GetHeight() = %d", client.m_last_tip->GetHeight()) : "") << std::endl << + "chainActive.Tip()->GetHeight() = " << chainActive.Tip()->GetHeight() << std::endl << + "client.m_supports_extranonce = " << client.m_supports_extranonce << std::endl << + "client.m_send_work = " << client.m_send_work << std::endl << + std::endl; + } + + // Ignore clients that aren't authorized yet. + if (!client.m_authorized && client.m_aux_addr.empty()) { + continue; + } + + if ( (client.m_last_tip && client.m_last_tip->GetHeight() == chainActive.Tip()->GetHeight()) || (!client.m_last_tip) ) + { + mempool.AddTransactionsUpdated(1); + client.m_last_tip = (client.m_last_tip ? nullptr : chainActive.Tip()); + cvBlockChange.notify_all(); + } + + std::string data = ""; + + try { + data = GetWorkUnit(client); + + UniValue json_client(UniValue::VOBJ); + json_client.push_back(Pair("addr", client.m_addr.ToString())); + json_client.push_back(Pair("service", client.m_from.ToString())); + + json_clients.push_back(json_client); + + } catch (const UniValue& objError) { + data = JSONRPCReply(NullUniValue, objError, NullUniValue); + skipped++; + } catch (const std::exception& e) { + // Some sort of error. Ignore. + std::string msg = strprintf("Error generating updated work for stratum client: %s", e.what()); + LogPrint("stratum", "%s\n", msg); + data = JSONRPCReply(NullUniValue, JSONRPCError(RPC_INTERNAL_ERROR, msg), NullUniValue); + skipped++; + } + + assert(output); + if (evbuffer_add(output, data.data(), data.size())) { + LogPrint("stratum", "Sending stratum work unit failed. (Reason: %d, '%s')\n", errno, evutil_socket_error_to_string(errno)); + } + } + } + // else { + // throw JSONRPCError(RPC_INTERNAL_ERROR, "Something went wrong, plz try again!"); + // } + + obj.push_back(Pair("clients", json_clients)); + obj.push_back(Pair("updated", json_clients.size())); + obj.push_back(Pair("skipped", skipped)); + obj.push_back(Pair("total", subscriptions.size())); + + return obj; +} + +UniValue rpc_stratum_getdifficulty (const UniValue& params, bool fHelp, const CPubKey& mypk) { + + if (fHelp || params.size() != 0) + throw std::runtime_error( + "stratum_getdifficulty\n" + "Show the current local diff of a stratum port.\n" + "\nExamples:\n" + + HelpExampleCli("stratum_getdifficulty", "") + + HelpExampleRpc("stratum_getdifficulty", "") + ); + + UniValue obj(UniValue::VOBJ); + + arith_uint256 aHashTarget = instance_of_cstratumparams.getTarget(); + std::string strTarget = aHashTarget.GetHex(); + + CBlockIndex tmp_index; + tmp_index.nBits = arith_uint256(strTarget).GetCompact(); + double kmd_diff = GetDifficulty(&tmp_index); + double ccminer_diff = ccminer::equi_stratum_target_to_diff(strTarget); + + obj.push_back(Pair("target", strTarget)); + obj.push_back(Pair("target_compact", strprintf("%08x",tmp_index.nBits))); + obj.push_back(Pair("kmd_diff_str", strprintf("%g",kmd_diff))); + obj.push_back(Pair("ccminer_diff_str", strprintf("%g", ccminer_diff))); + obj.push_back(Pair("kmd_diff", kmd_diff)); + obj.push_back(Pair("ccminer_diff", ccminer_diff)); + + return obj; +}; + +UniValue rpc_stratum_setdifficulty (const UniValue& params, bool fHelp, const CPubKey& mypk) { + + /* + https://bitcoin.stackexchange.com/questions/30467/what-are-the-equations-to-convert-between-bits-and-difficulty + + There are 3 representations of the same thing (with varying degrees of precision) in Bitcoin: + + - bits - unsigned int 32-bit + - target - unsigned int 256-bit + - difficulty - double-precision float (64-bit) + + and 6 methods are necessary to convert between any two of these: + + - bits -> target (SetCompact() in bitcoin/src/arith_uint256.cpp) + - bits -> difficulty (GetDifficulty() in bitcoin/src/rpc/blockchain.cpp) + - target -> bits (GetCompact() in bitcoin/src/arith_uint256.cpp) + - target -> difficulty (same as target -> bits -> difficulty) + - difficulty -> bits (not done in bitcoin/src) -> we will use kmd_diff_to_target_equi for that + - difficulty -> target (same as difficulty -> bits -> target) + */ + if (fHelp || params.size() != 1) + throw std::runtime_error( + "stratum_setdifficulty\n" + "Set the diff on a stratum port.\n" + "\nExamples:\n" + + HelpExampleCli("stratum_setdifficulty", "") + + HelpExampleRpc("stratum_setdifficulty", "") + ); + + // diff can be accepted in two ways: as a hex target or as a kmd_diff, both variants assume + // passing a string with 32 bytes hex target or string (!) with a double value, or double + // value as a double + + double kmd_diff; // calculated value: diff_str -> kmd_diff + std::string diff_str = instance_of_cstratumparams.getTarget().ToString(); + + if (params[0].getType() == UniValue::VSTR) { + std::string param_str = params[0].get_str(); + if (IsHex(param_str) && param_str.size() == 64) { + // hex target passed + diff_str = param_str; + + } else { + if (ParseDouble(param_str, &kmd_diff)) { + // kmd diff as a str passed + + // difficulty = difficulty_1_target / current_target + arith_uint256 target; + ccminer::kmd_diff_to_target_equi((uint32_t *)&target, kmd_diff); + diff_str = target.ToString(); + + } else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid difficulty (not hex target, not kmd_diff)"); + } + } else if (params[0].getType() == UniValue::VNUM) { + // kmd diff as a num passed + kmd_diff = params[0].get_real(); + + // difficulty = difficulty_1_target / current_target + arith_uint256 target; + ccminer::kmd_diff_to_target_equi((uint32_t *)&target, kmd_diff); + diff_str = target.ToString(); + + } else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid difficulty"); + + instance_of_cstratumparams.setTarget(arith_uint256(diff_str)); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("target", diff_str)); + + CBlockIndex tmp_index; + tmp_index.nBits = arith_uint256(diff_str).GetCompact(); + + double new_kmd_diff = GetDifficulty(&tmp_index); + obj.push_back(Pair("kmd_diff_str", strprintf("%g",new_kmd_diff))); + obj.push_back(Pair("kmd_diff", new_kmd_diff)); + + return obj; +}; + +UniValue rpc_stratum_getclientscount (const UniValue& params, bool fHelp, const CPubKey& mypk) { + + if (fHelp || params.size() != 0) + throw std::runtime_error( + "stratum_getclientscount\n" + "Show the the number of stratum clients.\n" + "\nExamples:\n" + + HelpExampleCli("stratum_getclientscount", "") + + HelpExampleRpc("stratum_getclientscount", "") + ); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("total", subscriptions.size())); + + return obj; +}; + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + { "stratum", "stratum_updatework", &rpc_stratum_updatework, true }, + { "stratum", "stratum_getdifficulty", &rpc_stratum_getdifficulty, true }, + { "stratum", "stratum_setdifficulty", &rpc_stratum_setdifficulty, true }, + { "stratum", "stratum_getclientscount", &rpc_stratum_getclientscount, true }, +}; + +void RegisterStratumRPCCommands(CRPCTable &tableRPC) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); +} + +// End of File diff --git a/src/stratum.h b/src/stratum.h new file mode 100644 index 000000000..56dc52056 --- /dev/null +++ b/src/stratum.h @@ -0,0 +1,21 @@ +// Copyright (c) 2021-2021 The Hush developers +// Copyright (c) 2020-2021 The Freicoin Developers +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#ifndef HUSH_STRATUM_H +#define HUSH_STRATUM_H + +/** Configure the stratum server. */ +bool InitStratumServer(); + +/** Interrupt the stratum server connections. */ +void InterruptStratumServer(); + +/** Cleanup stratum server network connections and free resources. */ +void StopStratumServer(); + +#endif // HUSH_STRATUM_H + +// End of File From 7b8209b2579f3d6e1c7b4fe613583862fb43bd13 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 9 Jul 2021 23:35:38 -0400 Subject: [PATCH 2/7] . --- src/stratum.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/stratum.cpp b/src/stratum.cpp index 3619c9ea1..81042d2eb 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -670,7 +670,7 @@ std::string GetWorkUnit(StratumClient& client) } */ /* if (!Params().MineBlocksOnDemand() && g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) { - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Komodo is not connected!"); + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Hush is not connected!"); } */ bool fvNodesEmpty; @@ -681,15 +681,15 @@ std::string GetWorkUnit(StratumClient& client) if (Params().MiningRequiresPeers() && fvNodesEmpty) { - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Komodo is not connected!"); + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Hush is not connected!"); } if (IsInitialBlockDownload()) { - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Komodo is downloading blocks..."); + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Hush is downloading blocks..."); } if (!client.m_authorized && client.m_aux_addr.empty()) { - throw JSONRPCError(RPC_INVALID_REQUEST, "Stratum client not authorized. Use mining.authorize first, with a Komodo address as the username."); + throw JSONRPCError(RPC_INVALID_REQUEST, "Stratum client not authorized. Use mining.authorize first, with a Hush R.. address as the username."); } static CBlockIndex* tip = NULL; // pindexPrev @@ -798,7 +798,7 @@ std::string GetWorkUnit(StratumClient& client) /* // Targets Table Example: komodo diff and ccminer diff are different (!), - // Komodo diff = NiceHash diff, ccminer_diff = Yiimp diff. + // Hush diff = NiceHash diff, ccminer_diff = Yiimp diff. hashTarget.SetCompact(HUSH_MINDIFF_NBITS,&fNegative,&fOverflow); // blkhdr.nBits hashTarget = UintToArith256(uint256S("0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f")); @@ -1245,7 +1245,7 @@ UniValue stratum_mining_authorize(StratumClient& client, const UniValue& params) CBitcoinAddress addr(get_stripped_username(username)); if (!addr.IsValid()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid Komodo address: %s", username)); + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid Hush address: %s", username)); } client.m_addr = addr; From 60a34af810f4dfe91a17e980efe8872d5056d5df Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 9 Jul 2021 23:41:44 -0400 Subject: [PATCH 3/7] The meta-revert of https://github.com/tradecraftio/tradecraft/commit/b0f2fa3399de8fc9b5a9dd5452570f53dace9848 --- src/stratum.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stratum.cpp b/src/stratum.cpp index 81042d2eb..97179303e 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -1111,6 +1111,8 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork //if (instance_of_cstratumparams.fstdErrDebugOutput) std::cerr << DateTimeStrPrecise() << "res[2] = " << res << std::endl; + // we haven't PreciousBlock, so we can't prioritize the block this way for now + /* if (res) { // LOCK(cs_main); if (!mapBlockIndex.count(hash)) { @@ -1119,7 +1121,6 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork { CBlockIndex* block_index = mapBlockIndex.at(hash); CValidationState state; - // we haven't PreciousBlock, so we can't prioritize the block this way for now // PreciousBlock(state, Params(), block_index); // if (!state.IsValid()) { @@ -1127,6 +1128,7 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork // } } } + */ } else { LogPrintf("NEW SHARE!!! by %s: %s\n", client.m_addr.ToString(), hash.ToString()); } From 7a9dfb89a73d80015a2f862d4e8d6b47d2fa04bc Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Mon, 9 Aug 2021 12:30:36 -0400 Subject: [PATCH 4/7] Debug for -testnode=1 --- src/chainparams.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index a26f8955c..26f4e8488 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -542,8 +542,11 @@ void *chainparams_commandline() { pCurrentParams->SetNValue(ASSETCHAINS_NK[0]); pCurrentParams->SetKValue(ASSETCHAINS_NK[1]); } - if ( HUSH_TESTNODE != 0 ) + if ( HUSH_TESTNODE != 0 ) { + fprintf(stderr,"%s: This is a test node, mining will not require peers!\n", __func__); pCurrentParams->SetMiningRequiresPeers(false); + } + if ( ASSETCHAINS_RPCPORT == 0 ) ASSETCHAINS_RPCPORT = ASSETCHAINS_P2PPORT + 1; pCurrentParams->pchMessageStart[0] = ASSETCHAINS_MAGIC & 0xff; From b84883d9b9a3aec00158280937e36107b1a49f3b Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Mon, 9 Aug 2021 21:04:20 -0400 Subject: [PATCH 5/7] Hushify stratum --- src/stratum.cpp | 68 ++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/src/stratum.cpp b/src/stratum.cpp index 97179303e..9bbeb1a12 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -65,7 +65,7 @@ #include -extern uint16_t ASSETCHAINS_RPCPORT; // don't want to include komodo_globals.h +extern uint16_t ASSETCHAINS_RPCPORT; // don't want to include hush_globals.h UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); // rpc/blockchain.cpp bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); // src/core_read.cpp @@ -286,7 +286,7 @@ namespace ccminer { } } - void kmd_diff_to_target_equi(uint32_t *target, double diff) + void hush_diff_to_target_equi(uint32_t *target, double diff) { uint64_t m; int k; @@ -797,27 +797,27 @@ std::string GetWorkUnit(StratumClient& client) arith_uint256 hashTarget; bool fNegative,fOverflow; /* - // Targets Table Example: komodo diff and ccminer diff are different (!), + // Targets Table Example: hush diff and ccminer diff are different (!), // Hush diff = NiceHash diff, ccminer_diff = Yiimp diff. hashTarget.SetCompact(HUSH_MINDIFF_NBITS,&fNegative,&fOverflow); // blkhdr.nBits hashTarget = UintToArith256(uint256S("0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f")); hashTarget.SetHex("0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); - hashTarget.SetHex("00ffff0000000000000000000000000000000000000000000000000000000000"); // komodo_diff = 15.0591, ccminer_diff = 1 - hashTarget.SetHex("003fffc000000000000000000000000000000000000000000000000000000000"); // komodo_diff = 60.2362, ccminer_diff = 4 - hashTarget.SetHex("0007fff800000000000000000000000000000000000000000000000000000000"); // komodo_diff = 481.89, ccminer_diff = 31.9999 - hashTarget.SetHex("c7ff3800ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // komodo_diff = 0.0752956, ccminer_diff = 1.00303 - hashTarget.SetHex("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); // komodo_diff = 1, ccminer_diff = 16.9956 + hashTarget.SetHex("00ffff0000000000000000000000000000000000000000000000000000000000"); // hush_diff = 15.0591, ccminer_diff = 1 + hashTarget.SetHex("003fffc000000000000000000000000000000000000000000000000000000000"); // hush_diff = 60.2362, ccminer_diff = 4 + hashTarget.SetHex("0007fff800000000000000000000000000000000000000000000000000000000"); // hush_diff = 481.89, ccminer_diff = 31.9999 + hashTarget.SetHex("c7ff3800ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // hush_diff = 0.0752956, ccminer_diff = 1.00303 + hashTarget.SetHex("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); // hush_diff = 1, ccminer_diff = 16.9956 */ arith_uint256 aHashTarget = instance_of_cstratumparams.getTarget(); // arith_uint256 aHashTarget = UintToArith256(uint256S("00ffff0000000000000000000000000000000000000000000000000000000000")); // 1.0 - // aHashTarget = aHashTarget / 8704; // komodo_diff = 131074 (NiceHash), ccminer_diff = 8704 (Yiimp) + // aHashTarget = aHashTarget / 8704; // hush_diff = 131074 (NiceHash), ccminer_diff = 8704 (Yiimp) /* here we can adjust diff by some algo, note that 00ffff0000000000000000000000000000000000000000000000000000000000 / 8704 = - 0000078780000000000000000000000000000000000000000000000000000000, which is equivalent to komodo_diff = 131074 (NiceHash), + 0000078780000000000000000000000000000000000000000000000000000000, which is equivalent to hush_diff = 131074 (NiceHash), ccminer_diff = 8704 (Yiimp) */ @@ -827,7 +827,7 @@ std::string GetWorkUnit(StratumClient& client) if (instance_of_cstratumparams.fstdErrDebugOutput) { std::cerr << DateTimeStrPrecise() << __func__ << ": " << __FILE__ << "," << __LINE__ << - strprintf(" target = %s, komodo_diff = %g, ccminer_diff = %g", + strprintf(" target = %s, hush_diff = %g, ccminer_diff = %g", strTarget, GetDifficultyFromBits(hashTarget.GetCompact(false)), ccminer::equi_stratum_target_to_diff(strTarget)) << std::endl; } } @@ -1022,11 +1022,11 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork CBlockIndex tmp_index; tmp_index.nBits = blkhdr.nBits; - double kmd_target_diff = GetDifficulty(&tmp_index); // diff from nbits (target) + double hush_target_diff = GetDifficulty(&tmp_index); // diff from nbits (target) tmp_index.nBits = UintToArith256(hash).GetCompact(); - double kmd_real_diff = GetDifficulty(&tmp_index); // real diff (from hash) + double hush_real_diff = GetDifficulty(&tmp_index); // real diff (from hash) tmp_index.nBits = arith_uint256(current_work.local_diff).GetCompact(); - double kmd_local_diff = GetDifficulty(&tmp_index); // local diff (from local port diff) + double hush_local_diff = GetDifficulty(&tmp_index); // local diff (from local port diff) double ccminer_real_diff = ccminer::equi_stratum_target_to_diff(hash.ToString()); double ccminer_target_diff = ccminer::equi_stratum_target_to_diff(arith_uint256().SetCompact(blkhdr.nBits).ToString()); @@ -1040,7 +1040,7 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork // TODO: we need to check hash > local port diff, and if it's true -> throw an exception -> diff too low (!) if (!instance_of_cstratumparams.fAllowLowDiffShares) if (UintToArith256(blkhdr.GetHash()) > arith_uint256(current_work.local_diff)) - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Low diff share (diff %g, local %g)", kmd_real_diff, kmd_local_diff)); + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Low diff share (diff %g, local %g)", hush_real_diff, hush_local_diff)); if (finish > start) { @@ -1056,12 +1056,10 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork std::cerr << DateTimeStrPrecise() << strprintf("%saccepted: %" PRIu64 "/%" PRIu64 "%s ", ColorTypeNames[cl_WHT], counter_TotalBlocks, counter_TotalShares, ColorTypeNames[cl_N] ); if (fDisplayDiffKMD) { - /* komodod diff display */ - std::cerr << strprintf("%slocal %g%s ", "\x1B[90m", kmd_local_diff, ColorTypeNames[cl_N]) << - strprintf("%s(diff %g, target %g) %s ", ColorTypeNames[cl_WHT], kmd_real_diff, kmd_target_diff, ColorTypeNames[cl_N]); - } - else - { /* ccminer diff display */ + /* hushd diff display */ + std::cerr << strprintf("%slocal %g%s ", "\x1B[90m", hush_local_diff, ColorTypeNames[cl_N]) << + strprintf("%s(diff %g, target %g) %s ", ColorTypeNames[cl_WHT], hush_real_diff, hush_target_diff, ColorTypeNames[cl_N]); + } else { /* ccminer diff display */ std::cerr << strprintf("%slocal %.3f%s ", "\x1B[90m", ccminer_local_diff, ColorTypeNames[cl_N]) << strprintf("%s(diff %.3f, target %.3f) %s", ColorTypeNames[cl_WHT], ccminer_real_diff, ccminer_target_diff, ColorTypeNames[cl_N]); // ccminer diff } @@ -2005,14 +2003,14 @@ UniValue rpc_stratum_getdifficulty (const UniValue& params, bool fHelp, const CP CBlockIndex tmp_index; tmp_index.nBits = arith_uint256(strTarget).GetCompact(); - double kmd_diff = GetDifficulty(&tmp_index); + double hush_diff = GetDifficulty(&tmp_index); double ccminer_diff = ccminer::equi_stratum_target_to_diff(strTarget); obj.push_back(Pair("target", strTarget)); obj.push_back(Pair("target_compact", strprintf("%08x",tmp_index.nBits))); - obj.push_back(Pair("kmd_diff_str", strprintf("%g",kmd_diff))); + obj.push_back(Pair("hush_diff_str", strprintf("%g",hush_diff))); obj.push_back(Pair("ccminer_diff_str", strprintf("%g", ccminer_diff))); - obj.push_back(Pair("kmd_diff", kmd_diff)); + obj.push_back(Pair("hush_diff", hush_diff)); obj.push_back(Pair("ccminer_diff", ccminer_diff)); return obj; @@ -2035,7 +2033,7 @@ UniValue rpc_stratum_setdifficulty (const UniValue& params, bool fHelp, const CP - bits -> difficulty (GetDifficulty() in bitcoin/src/rpc/blockchain.cpp) - target -> bits (GetCompact() in bitcoin/src/arith_uint256.cpp) - target -> difficulty (same as target -> bits -> difficulty) - - difficulty -> bits (not done in bitcoin/src) -> we will use kmd_diff_to_target_equi for that + - difficulty -> bits (not done in bitcoin/src) -> we will use hush_diff_to_target_equi for that - difficulty -> target (same as difficulty -> bits -> target) */ if (fHelp || params.size() != 1) @@ -2047,11 +2045,11 @@ UniValue rpc_stratum_setdifficulty (const UniValue& params, bool fHelp, const CP + HelpExampleRpc("stratum_setdifficulty", "") ); - // diff can be accepted in two ways: as a hex target or as a kmd_diff, both variants assume + // diff can be accepted in two ways: as a hex target or as a hush_diff, both variants assume // passing a string with 32 bytes hex target or string (!) with a double value, or double // value as a double - double kmd_diff; // calculated value: diff_str -> kmd_diff + double hush_diff; // calculated value: diff_str -> hush_diff std::string diff_str = instance_of_cstratumparams.getTarget().ToString(); if (params[0].getType() == UniValue::VSTR) { @@ -2061,24 +2059,24 @@ UniValue rpc_stratum_setdifficulty (const UniValue& params, bool fHelp, const CP diff_str = param_str; } else { - if (ParseDouble(param_str, &kmd_diff)) { + if (ParseDouble(param_str, &hush_diff)) { // kmd diff as a str passed // difficulty = difficulty_1_target / current_target arith_uint256 target; - ccminer::kmd_diff_to_target_equi((uint32_t *)&target, kmd_diff); + ccminer::hush_diff_to_target_equi((uint32_t *)&target, hush_diff); diff_str = target.ToString(); } else - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid difficulty (not hex target, not kmd_diff)"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid difficulty (not hex target, not hush_diff)"); } } else if (params[0].getType() == UniValue::VNUM) { // kmd diff as a num passed - kmd_diff = params[0].get_real(); + hush_diff = params[0].get_real(); // difficulty = difficulty_1_target / current_target arith_uint256 target; - ccminer::kmd_diff_to_target_equi((uint32_t *)&target, kmd_diff); + ccminer::hush_diff_to_target_equi((uint32_t *)&target, hush_diff); diff_str = target.ToString(); } else @@ -2092,9 +2090,9 @@ UniValue rpc_stratum_setdifficulty (const UniValue& params, bool fHelp, const CP CBlockIndex tmp_index; tmp_index.nBits = arith_uint256(diff_str).GetCompact(); - double new_kmd_diff = GetDifficulty(&tmp_index); - obj.push_back(Pair("kmd_diff_str", strprintf("%g",new_kmd_diff))); - obj.push_back(Pair("kmd_diff", new_kmd_diff)); + double new_hush_diff = GetDifficulty(&tmp_index); + obj.push_back(Pair("hush_diff_str", strprintf("%g",new_hush_diff))); + obj.push_back(Pair("hush_diff", new_hush_diff)); return obj; }; From 0b6d76616320e8ace5e8a22f82a9f880a7994217 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Mon, 9 Aug 2021 21:30:29 -0400 Subject: [PATCH 6/7] More useful stratum thread names --- src/stratum.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stratum.cpp b/src/stratum.cpp index 9bbeb1a12..a74583634 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -1176,7 +1176,7 @@ UniValue stratum_mining_subscribe(StratumClient& client, const UniValue& params) // Please note that Stratum use hex encoding, so you have to convert NONCE_1 from hex to binary before. // ["CONNECT_HOST", CONNECT_PORT, "MINER_USER_AGENT", "SESSION_ID"] - // ["NiceHash/1.0.0", null, "stratum3.decker.host", 18776] // ua, session_id, host, port? + // ["NiceHash/1.0.0", null, "stratum.hush.is", 28030] // ua, session_id, host, port? // ["ccminer/2.3.1"] UniValue ret(UniValue::VARR); @@ -1622,7 +1622,7 @@ static bool g_shutdown = false; void BlockWatcher() { - RenameThread("blkwatcher"); + RenameThread("hush-stratum-blkwatcher"); boost::unique_lock lock(csBestBlock); boost::system_time checktxtime; boost::system_time starttime = boost::get_system_time(); @@ -1735,7 +1735,7 @@ void BlockWatcher() void SendKeepAlivePackets() { - RenameThread("sockklv"); + RenameThread("hush-stratum-keepalive"); while (true) { // Run the notifier on an integer second in the steady clock. auto now = std::chrono::steady_clock::now().time_since_epoch(); From e6a650ff3ed564342daf3d5bb5a46cb03643431d Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Tue, 10 Aug 2021 12:50:34 -0400 Subject: [PATCH 7/7] Add some stratum spices --- src/init.cpp | 2 +- src/stratum.cpp | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 337cd3997..96aecf308 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -512,7 +512,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0)); strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)"); } - string debugCategories = "addrman, alert, bench, coindb, db, deletetx, estimatefee, http, libevent, lock, mempool, net, tls, partitioncheck, pow, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these + string debugCategories = "addrman, alert, bench, coindb, db, deletetx, estimatefee, http, libevent, lock, mempool, net, tls, partitioncheck, pow, proxy, prune, rand, reindex, rpc, selectcoins, stratum, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these strUsage += HelpMessageOpt("-debug=", strprintf(_("Output debugging information (default: %u, supplying is optional)"), 0) + ". " + _("If is not supplied or if = 1, output all debugging information.") + " " + _(" can be:") + " " + debugCategories + "."); strUsage += HelpMessageOpt("-experimentalfeatures", _("Enable use of experimental features")); diff --git a/src/stratum.cpp b/src/stratum.cpp index a74583634..233277520 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -312,10 +312,10 @@ namespace ccminer { /* compute nbits to get the network diff */ double equi_network_diff(uint32_t nbits) { - //KMD bits: "1e 015971", - //KMD target: "00 00 015971000000000000000000000000000000000000000000000000000000", - //KMD bits: "1d 686aaf", - //KMD target: "00 0000 686aaf0000000000000000000000000000000000000000000000000000", + //HUSH bits: "1e 015971", + //HUSH target: "00 00 015971000000000000000000000000000000000000000000000000000000", + //HUSH bits: "1d 686aaf", + //HUSH target: "00 0000 686aaf0000000000000000000000000000000000000000000000000000", // uint32_t nbits = work->data[26]; uint32_t bits = (nbits & 0xffffff); @@ -1051,11 +1051,11 @@ bool SubmitBlock(StratumClient& client, const uint256& job_id, const StratumWork // std::cerr << strprintf("%f ms - %" PRIu64 "", elapsed.count(), shares_accepted_since_last) << std::endl; } - bool fDisplayDiffKMD = true; // otherwise it will display ccminer diff + bool fDisplayDiffHUSH = true; // otherwise it will display ccminer diff std::cerr << DateTimeStrPrecise() << strprintf("%saccepted: %" PRIu64 "/%" PRIu64 "%s ", ColorTypeNames[cl_WHT], counter_TotalBlocks, counter_TotalShares, ColorTypeNames[cl_N] ); - if (fDisplayDiffKMD) { + if (fDisplayDiffHUSH) { /* hushd diff display */ std::cerr << strprintf("%slocal %g%s ", "\x1B[90m", hush_local_diff, ColorTypeNames[cl_N]) << strprintf("%s(diff %g, target %g) %s ", ColorTypeNames[cl_WHT], hush_real_diff, hush_target_diff, ColorTypeNames[cl_N]); @@ -1205,7 +1205,7 @@ UniValue stratum_mining_subscribe(StratumClient& client, const UniValue& params) ret.push_back(NullUniValue); ret.push_back(sExtraNonce1); - // On mining.subscribe we don't need ti send anything else, we will send + // On mining.subscribe we don't need to send anything else, we will send // mining.set_target and mining.notify bit later, inside GetWorkUnit. // Scheme is the following: // 1. stratum_read_cb(bufferevent * bev, void * ctx) @@ -1417,7 +1417,6 @@ static void stratum_read_cb(bufferevent *bev, void *ctx) free(cstr); LogPrint("stratum", "Received stratum request from %s : %s\n", client.GetPeer().ToString(), line); - //JSONRPCRequest jreq; JSONRequest jreq; std::string reply; @@ -1426,11 +1425,11 @@ static void stratum_read_cb(bufferevent *bev, void *ctx) UniValue valRequest; if (!valRequest.read(line)) { // Not JSON; is this even a stratum miner? - throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + throw JSONRPCError(RPC_PARSE_ERROR, strprintf("Invalid JSON, Parse error on: %s",line) ); } if (!valRequest.isObject()) { // Not a JSON object; don't know what to do. - throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); + throw JSONRPCError(RPC_PARSE_ERROR, "Not a JSON object"); } if (valRequest.exists("result")) { // JSON-RPC reply. Ignore. @@ -1809,7 +1808,10 @@ void SendKeepAlivePackets() bool InitStratumServer() { LOCK(cs_stratum); - fprintf(stderr,"%s: Starting built-in stratum server\n",__func__); + + int stratumPort = BaseParams().StratumPort(); + int defaultPort = GetArg("-stratumport", stratumPort); + fprintf(stderr,"%s: Starting built-in stratum server on port %d\n",__func__, defaultPort ); if (!InitStratumAllowList(stratum_allow_subnets)) { @@ -1834,14 +1836,12 @@ bool InitStratumServer() LogPrint("stratum", "Initialized stratum server\n"); } - stratum_method_dispatch["mining.subscribe"] = stratum_mining_subscribe; - stratum_method_dispatch["mining.authorize"] = stratum_mining_authorize; - stratum_method_dispatch["mining.configure"] = stratum_mining_configure; - stratum_method_dispatch["mining.submit"] = stratum_mining_submit; - stratum_method_dispatch["mining.extranonce.subscribe"] = - stratum_mining_extranonce_subscribe; - stratum_method_dispatch["mining.multi_version"] = - stratum_mining_multi_version; + stratum_method_dispatch["mining.subscribe"] = stratum_mining_subscribe; + stratum_method_dispatch["mining.authorize"] = stratum_mining_authorize; + stratum_method_dispatch["mining.configure"] = stratum_mining_configure; + stratum_method_dispatch["mining.submit"] = stratum_mining_submit; + stratum_method_dispatch["mining.extranonce.subscribe"] = stratum_mining_extranonce_subscribe; + stratum_method_dispatch["mining.multi_version"] = stratum_mining_multi_version; // Start thread to wait for block notifications and send updated @@ -1902,7 +1902,6 @@ UniValue rpc_stratum_updatework(const UniValue& params, bool fHelp, const CPubKe uint64_t skipped = 0; // send updated work to miners - // if (cs_stratum.try_lock()) { LOCK(cs_stratum); @@ -1935,6 +1934,7 @@ UniValue rpc_stratum_updatework(const UniValue& params, bool fHelp, const CPubKe // Ignore clients that aren't authorized yet. if (!client.m_authorized && client.m_aux_addr.empty()) { + fprintf(stderr,"%s: Ignoring unauthorized client\n", __func__); continue; }