├── src ├── src.kdev4 ├── util.h ├── cmake │ ├── Findleveldb.cmake │ ├── Findconfig4cpp.cmake │ ├── Findpoolcore.cmake │ └── Findlibp2p.cmake ├── block.h ├── stratum.h ├── equihash.tcc ├── address.h ├── CMakeLists.txt ├── protocol.proto ├── zcashpool.h ├── stratum.cpp ├── equihash_original.h ├── equihash_original.cpp ├── bignum.h ├── zcashpool.cpp ├── main.cpp └── uint256.h ├── poolrestapi.cfg.example ├── nginx.conf.example ├── zcash.cfg.example ├── pool.diff └── README.md /src/src.kdev4: -------------------------------------------------------------------------------- 1 | [Project] 2 | Manager=KDevCMakeManager 3 | Name=pool_frontend_zcash 4 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #include "uint256.h" 2 | #include 3 | #include 4 | 5 | static const int PROTOCOL_VERSION = 70001; 6 | -------------------------------------------------------------------------------- /src/cmake/Findleveldb.cmake: -------------------------------------------------------------------------------- 1 | find_library(LEVELDB_LIBRARY leveldb 2 | PATH ${ROOT_SOURCE_DIR}/leveldb 3 | ) 4 | 5 | find_path(LEVELDB_INCLUDE_DIR "leveldb/db.h" 6 | PATH ${ROOT_SOURCE_DIR}/leveldb/include 7 | ) 8 | -------------------------------------------------------------------------------- /src/cmake/Findconfig4cpp.cmake: -------------------------------------------------------------------------------- 1 | find_library(CONFIG4CPP_LIBRARY config4cpp 2 | PATH ${ROOT_SOURCE_DIR}/config4cpp/lib 3 | ) 4 | 5 | find_path(CONFIG4CPP_INCLUDE_DIR "config4cpp/Configuration.h" 6 | PATH ${ROOT_SOURCE_DIR}/config4cpp/include 7 | ) 8 | -------------------------------------------------------------------------------- /poolrestapi.cfg.example: -------------------------------------------------------------------------------- 1 | poolrestapi { 2 | listenAddress = "cxxrestapi://127.0.0.1:19999"; 3 | coins = ["xpm", "zcash"]; 4 | } 5 | 6 | xpm { 7 | frontends = ["p2p://127.0.0.1:13300"]; 8 | poolAppName = "pool_frontend_xpm"; 9 | } 10 | 11 | zcash { 12 | frontends = ["p2p://127.0.0.1:13301"]; 13 | poolAppName = "pool_frontend_zcash"; 14 | } -------------------------------------------------------------------------------- /src/block.h: -------------------------------------------------------------------------------- 1 | #include "bignum.h" 2 | #include 3 | #include "p2putils/xmstream.h" 4 | 5 | #pragma pack(push, 1) 6 | struct CBlockHeader { 7 | // header 8 | static const size_t HEADER_SIZE=4+32+32+32+4+4+32; // excluding Equihash solution 9 | static const int32_t CURRENT_VERSION=4; 10 | 11 | struct { 12 | int32_t nVersion; 13 | uint256 hashPrevBlock; 14 | uint256 hashMerkleRoot; 15 | uint256 hashReserved; 16 | uint32_t nTime; 17 | uint32_t nBits; 18 | } data; 19 | 20 | uint256 nNonce; 21 | std::vector nSolution; 22 | }; 23 | #pragma pack(pop) 24 | -------------------------------------------------------------------------------- /src/cmake/Findpoolcore.cmake: -------------------------------------------------------------------------------- 1 | set(BUILD_DIR "${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}") 2 | 3 | find_library(POOLCORE_LIBRARY poolcore 4 | PATH ${ROOT_SOURCE_DIR}/poolcore/${BUILD_DIR}/poolcore 5 | ) 6 | 7 | find_library(POOLCOMMON_LIBRARY poolcommon 8 | PATH ${ROOT_SOURCE_DIR}/poolcore/${BUILD_DIR}/poolcommon 9 | ) 10 | 11 | find_library(POOLCORE_LOGURU_LIBRARY loguru 12 | PATH ${ROOT_SOURCE_DIR}/poolcore/${BUILD_DIR} 13 | ) 14 | 15 | find_path(POOLCORE_INCLUDE_DIR "poolcore/backend.h" 16 | PATH ${ROOT_SOURCE_DIR}/poolcore/src/include 17 | ) 18 | 19 | set(POOLCORE_INCLUDE_DIR 20 | ${POOLCORE_INCLUDE_DIR} ${POOLCORE_INCLUDE_DIR}/../../${BUILD_DIR} 21 | ) 22 | -------------------------------------------------------------------------------- /src/cmake/Findlibp2p.cmake: -------------------------------------------------------------------------------- 1 | set(BUILD_DIR "${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}") 2 | 3 | find_library(AIO_LIBRARY asyncio-0.4 4 | PATH ${ROOT_SOURCE_DIR}/libp2p/${BUILD_DIR}/asyncio 5 | ) 6 | 7 | find_library(AIO_EXTRAS_LIBRARY asyncioextras-0.4 8 | PATH ${ROOT_SOURCE_DIR}/libp2p/${BUILD_DIR}/asyncioextras 9 | ) 10 | 11 | find_library(P2P_LIBRARY p2p 12 | PATH ${ROOT_SOURCE_DIR}/libp2p/${BUILD_DIR}/p2p 13 | ) 14 | 15 | find_library(P2PUTILS_LIBRARY p2putils 16 | PATH ${ROOT_SOURCE_DIR}/libp2p/${BUILD_DIR}/p2putils 17 | ) 18 | 19 | find_path(AIO_INCLUDE_DIR "asyncio/asyncio.h" 20 | PATH ${ROOT_SOURCE_DIR}/libp2p/src/include 21 | ) 22 | 23 | set(AIO_INCLUDE_DIR 24 | ${AIO_INCLUDE_DIR} ${AIO_INCLUDE_DIR}/../../${BUILD_DIR}/include 25 | ) 26 | 27 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 28 | set(AIO_LIBRARY ${AIO_LIBRARY} rt) 29 | endif() 30 | -------------------------------------------------------------------------------- /nginx.conf.example: -------------------------------------------------------------------------------- 1 | worker_processes 4; 2 | 3 | events { 4 | worker_connections 10240; 5 | multi_accept on; 6 | } 7 | 8 | 9 | http { 10 | include mime.types; 11 | default_type application/octet-stream; 12 | 13 | sendfile on; 14 | tcp_nopush on; 15 | tcp_nodelay on; 16 | 17 | keepalive_timeout 65; 18 | 19 | upstream api_backend { 20 | server 127.0.0.1:19999; 21 | keepalive 32; 22 | } 23 | 24 | server { 25 | listen *:80 reuseport; 26 | server_name pool; 27 | 28 | location / { 29 | root html; 30 | index index.html index.htm; 31 | expires 5s; 32 | } 33 | 34 | location /api { 35 | cxxrest_pass api_backend; 36 | } 37 | 38 | location ~* ^.+\.(jpg|jpeg|gif|png|ico)$ { 39 | expires 3d; # 3 days 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /zcash.cfg.example: -------------------------------------------------------------------------------- 1 | pool_frontend_zcash { 2 | isMaster = "true"; 3 | poolFee = "1"; 4 | poolFeeAddr = "t1ZsLopJKzyuaqmeC2Y6cx1351G13st2sWN"; 5 | walletAddrs = ["p2p://127.0.0.1:12201"]; 6 | localAddress = "p2p://127.0.0.1:13301"; 7 | walletAppName = "pool_rpc"; 8 | poolAppName = "pool_frontend_zcash"; 9 | requiredConfirmations = "10"; 10 | defaultMinimalPayout = "0.01"; 11 | minimalPayout = "0.001"; 12 | dbPath = "/home/xpm/pool.zcash"; 13 | keepRoundTime = "3"; 14 | keepStatsTime = "2"; 15 | confirmationsCheckInterval = "7"; 16 | payoutInterval = "30"; 17 | balanceCheckInterval = "3"; 18 | statisticCheckInterval = "1"; 19 | checkAddress = "true"; 20 | shareTarget = "2048"; 21 | equihashShareCheck = "true"; 22 | stratumWorkLifeTime = "9"; 23 | 24 | zmqclientHost = "coinsforall.io"; 25 | zmqclientListenPort = "6668"; 26 | zmqclientWorkPort = "60200"; 27 | 28 | pool_zaddr = "zcWDMjkEyRPQAFjs5RWEzoqXG9BipLRWr4S9bC4A44g7eBv6ZhnqrWWc5M5MyNn4pt9vP2PEvK9NkUwerQbewEcEWz8ZL4f"; 29 | pool_taddr = "t1ZRsyK4pkBq7WD4gnF8r3nCYSQT19Zhh3C"; 30 | } 31 | -------------------------------------------------------------------------------- /src/stratum.h: -------------------------------------------------------------------------------- 1 | #ifndef __STRATUM_H_ 2 | #define __STRATUM_H_ 3 | 4 | #include "p2putils/xmstream.h" 5 | #include 6 | #include 7 | 8 | enum StratumMethodTy { 9 | Subscribe = 0, 10 | Authorize, 11 | ExtraNonceSubscribe, 12 | Submit, 13 | Last 14 | }; 15 | 16 | enum StratumDecodeStatusTy { 17 | Ok = 0, 18 | JsonError, 19 | FormatError 20 | }; 21 | 22 | struct StratumMiningSubscribe { 23 | std::string minerUserAgent; 24 | std::string sessionId; 25 | std::string connectHost; 26 | int64_t connectPort; 27 | }; 28 | 29 | struct StratumAuthorize { 30 | std::string login; 31 | std::string password; 32 | }; 33 | 34 | struct StratumSubmit { 35 | std::string workerName; 36 | std::string jobId; 37 | unsigned time; 38 | uint8_t nonce[32]; 39 | std::vector equihashSolution; 40 | }; 41 | 42 | struct StratumMessage { 43 | int64_t id; 44 | StratumMethodTy method; 45 | 46 | StratumMiningSubscribe subscribe; 47 | StratumAuthorize authorize; 48 | StratumSubmit submit; 49 | 50 | std::string error; 51 | }; 52 | 53 | StratumDecodeStatusTy decodeStratumMessage(const char *in, StratumMessage *out); 54 | // void encodeStratumMessage(const StratumMessage *in, xmstream &out); 55 | 56 | void stratumLittleEndianHex(const char *in, size_t inSize, char *out); 57 | 58 | template 59 | void stratumDumpHex(T data, char *out) { 60 | char *o = out; 61 | for (unsigned i = 0; i < sizeof(T); i++) { 62 | char digit1 = (data & 0xF); 63 | char digit2 = (data>>4 & 0xF); 64 | char hexDigit1 = (digit1 <= 9) ? '0'+digit1 : 'A'+digit1-10; 65 | char hexDigit2 = (digit2 <= 9) ? '0'+digit2 : 'A'+digit2-10; 66 | *o++ = hexDigit2; 67 | *o++ = hexDigit1; 68 | data >>= 8; 69 | } 70 | *o = 0; 71 | } 72 | 73 | #endif //__STRATUM_H_ 74 | -------------------------------------------------------------------------------- /src/equihash.tcc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Jack Grigg 2 | // Copyright (c) 2016 The Zcash developers 3 | // Distributed under the MIT software license, see the accompanying 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | #include 7 | #include 8 | 9 | // Checks if the intersection of a.indices and b.indices is empty 10 | template 11 | bool DistinctIndices(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices) 12 | { 13 | for(size_t i = 0; i < lenIndices; i += sizeof(eh_index)) { 14 | for(size_t j = 0; j < lenIndices; j += sizeof(eh_index)) { 15 | if (memcmp(a.hash+len+i, b.hash+len+j, sizeof(eh_index)) == 0) { 16 | return false; 17 | } 18 | } 19 | } 20 | return true; 21 | } 22 | 23 | template 24 | bool IsProbablyDuplicate(std::shared_ptr indices, size_t lenIndices) 25 | { 26 | assert(lenIndices <= MAX_INDICES); 27 | bool checked_index[MAX_INDICES] = {false}; 28 | int count_checked = 0; 29 | for (int z = 0; z < lenIndices; z++) { 30 | // Skip over indices we have already paired 31 | if (!checked_index[z]) { 32 | for (int y = z+1; y < lenIndices; y++) { 33 | if (!checked_index[y] && indices.get()[z] == indices.get()[y]) { 34 | // Pair found 35 | checked_index[y] = true; 36 | count_checked += 2; 37 | break; 38 | } 39 | } 40 | } 41 | } 42 | return count_checked == lenIndices; 43 | } 44 | 45 | template 46 | bool IsValidBranch(const FullStepRow& a, const size_t len, const unsigned int ilen, const eh_trunc t) 47 | { 48 | return TruncateIndex(ArrayToEhIndex(a.hash+len), ilen) == t; 49 | } 50 | -------------------------------------------------------------------------------- /src/address.h: -------------------------------------------------------------------------------- 1 | #include "poolcore/base58.h" 2 | 3 | class CZECAddress : public CBase58Data { 4 | private: 5 | enum Base58Type { 6 | PUBKEY_ADDRESS, 7 | SCRIPT_ADDRESS, 8 | SECRET_KEY, 9 | EXT_PUBLIC_KEY, 10 | EXT_SECRET_KEY, 11 | 12 | ZCPAYMENT_ADDRRESS, 13 | ZCSPENDING_KEY, 14 | 15 | MAX_BASE58_TYPES 16 | }; 17 | 18 | public: 19 | bool SetString(const char* pszAddress) { CBase58Data::SetString(pszAddress, 2); } 20 | bool SetString(const std::string& strAddress) { SetString(strAddress.c_str()); } 21 | CZECAddress() {} 22 | CZECAddress(const std::string& strAddress) { SetString(strAddress); } 23 | CZECAddress(const char* pszAddress) { SetString(pszAddress); } 24 | 25 | bool isValid() { 26 | std::vector base58Prefixes[MAX_BASE58_TYPES]; 27 | // guarantees the first 2 characters, when base58 encoded, are "t1" 28 | base58Prefixes[PUBKEY_ADDRESS] = {0x1C,0xB8}; 29 | // guarantees the first 2 characters, when base58 encoded, are "t3" 30 | base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBD}; 31 | // the first character, when base58 encoded, is "5" or "K" or "L" (as in Bitcoin) 32 | base58Prefixes[SECRET_KEY] = {0x80}; 33 | // do not rely on these BIP32 prefixes; they are not specified and may change 34 | base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x88,0xB2,0x1E}; 35 | base58Prefixes[EXT_SECRET_KEY] = {0x04,0x88,0xAD,0xE4}; 36 | // guarantees the first 2 characters, when base58 encoded, are "zc" 37 | base58Prefixes[ZCPAYMENT_ADDRRESS] = {0x16,0x9A}; 38 | // guarantees the first 2 characters, when base58 encoded, are "SK" 39 | base58Prefixes[ZCSPENDING_KEY] = {0xAB,0x36}; 40 | 41 | bool fCorrectSize = vchData.size() == 20; 42 | bool fKnownVersion = vchVersion == base58Prefixes[PUBKEY_ADDRESS] || 43 | vchVersion == base58Prefixes[SCRIPT_ADDRESS]; 44 | return fCorrectSize && fKnownVersion; 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /pool.diff: -------------------------------------------------------------------------------- 1 | diff --git a/src/init.cpp b/src/init.cpp 2 | index 7a4fe6e..d640a9c 100644 3 | --- src/init.cpp 4 | +++ src/init.cpp 5 | @@ -58,6 +58,8 @@ 6 | 7 | using namespace std; 8 | 9 | +void *poolRpcThread(void *arg); 10 | + 11 | extern void ThreadSendAlert(); 12 | 13 | ZCJoinSplit* pzcashParams = NULL; 14 | @@ -1634,5 +1636,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) 15 | // SENDALERT 16 | threadGroup.create_thread(boost::bind(ThreadSendAlert)); 17 | 18 | + pthread_t thread; 19 | + pthread_create(&thread, 0, poolRpcThread, 0); 20 | return !fRequestShutdown; 21 | } 22 | diff --git a/src/main.cpp b/src/main.cpp 23 | index 207c0d4..9145d10 100644 24 | --- src/main.cpp 25 | +++ src/main.cpp 26 | @@ -39,6 +39,8 @@ 27 | 28 | using namespace std; 29 | 30 | +void *newBlockNotify(void *block); 31 | + 32 | #if defined(NDEBUG) 33 | # error "Zcash cannot be compiled without assertions." 34 | #endif 35 | @@ -2708,6 +2710,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { 36 | pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); 37 | } 38 | // Notify external listeners about the new tip. 39 | + newBlockNotify(pindexNewTip); 40 | GetMainSignals().UpdatedBlockTip(pindexNewTip); 41 | uiInterface.NotifyBlockTip(hashNewTip); 42 | } 43 | diff --git a/src/miner.cpp b/src/miner.cpp 44 | index 5e28115..08c0340 100644 45 | --- src/miner.cpp 46 | +++ src/miner.cpp 47 | @@ -456,9 +456,9 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& 48 | } 49 | 50 | #ifdef ENABLE_WALLET 51 | -static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) 52 | +bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) 53 | #else 54 | -static bool ProcessBlockFound(CBlock* pblock) 55 | +bool ProcessBlockFound(CBlock* pblock) 56 | #endif // ENABLE_WALLET 57 | { 58 | LogPrintf("%s\n", pblock->ToString()); 59 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(pool_frontend_zcash) 3 | 4 | set (CMAKE_CXX_STANDARD 11) 5 | option(SANITIZER_ENABLED "Build with address sanitizer" OFF) 6 | option(GPROF_ENABLED "Build with GNU profiler (use gprof ./exename -p > out.txt)" OFF) 7 | 8 | if (SANITIZER_ENABLED) 9 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") 10 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") 11 | endif() 12 | 13 | if (GPROF_ENABLED) 14 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg") 15 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") 16 | endif() 17 | 18 | set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} 19 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake 20 | ) 21 | 22 | find_package(Boost COMPONENTS filesystem thread system REQUIRED) 23 | find_package(Protobuf REQUIRED) 24 | find_package(OpenSSL REQUIRED) 25 | find_package(libp2p REQUIRED) 26 | find_package(poolcore REQUIRED) 27 | find_package(config4cpp REQUIRED) 28 | 29 | find_path(GMP_INCLUDE_DIRECTORY gmp.h gmpxx.h) 30 | find_library(GMP_LIBRARY gmp) 31 | find_library(GMPXX_LIBRARY gmpxx) 32 | 33 | find_library(LEVELDB_LIBRARY leveldb 34 | PATH ${ROOT_SOURCE_DIR}/leveldb-1.18 35 | ) 36 | 37 | find_path(LEVELDB_INCLUDE_DIR "leveldb/db.h" 38 | PATH ${ROOT_SOURCE_DIR}/leveldb-1.18/include 39 | ) 40 | 41 | find_path(JANSSON_INCLUDE_DIRECTORY jansson.h) 42 | find_library(JANSSON_LIBRARY jansson) 43 | 44 | find_path(SODIUM_INCLUDE_DIRECTORY sodium.h) 45 | find_library(SODIUM_LIBRARY sodium) 46 | 47 | PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${CMAKE_SOURCE_DIR}/protocol.proto) 48 | 49 | include_directories( 50 | ${AIO_INCLUDE_DIR} 51 | ${FLATBUFFERS_INCLUDE_DIR} 52 | ${GMP_INCLUDE_DIRECTORY} 53 | ${LEVELDB_INCLUDE_DIR} 54 | ${JANSSON_INCLUDE_DIRECTORY} 55 | ${POOLCORE_INCLUDE_DIR} 56 | ${PROTOBUF_INCLUDE_DIR} 57 | ${SODIUM_INCLUDE_DIRECTORY} 58 | ${CONFIG4CPP_INCLUDE_DIR} 59 | ${CMAKE_BINARY_DIR} 60 | ) 61 | 62 | add_executable(pool_frontend_zcash 63 | main.cpp 64 | zcashpool.cpp 65 | equihash_original.cpp 66 | stratum.cpp 67 | ${ProtoSources} 68 | ) 69 | 70 | target_link_libraries(pool_frontend_zcash 71 | ${POOLCOMMON_LIBRARY} 72 | ${POOLCORE_LIBRARY} 73 | ${POOLCORE_LOGURU_LIBRARY} 74 | ${AIO_LIBRARY} 75 | ${AIO_EXTRAS_LIBRARY} 76 | ${P2P_LIBRARY} 77 | ${P2PUTILS_LIBRARY} 78 | ${GMP_LIBRARY} 79 | ${GMPXX_LIBRARY} 80 | ${OPENSSL_SSL_LIBRARY} 81 | ${OPENSSL_CRYPTO_LIBRARY} 82 | ${LEVELDB_LIBRARY} 83 | pthread 84 | ${Boost_SYSTEM_LIBRARY} 85 | ${Boost_FILESYSTEM_LIBRARY} 86 | ${Boost_THREAD_LIBRARY} 87 | ${PROTOBUF_LIBRARY} 88 | ${CONFIG4CPP_LIBRARY} 89 | ${JANSSON_LIBRARY} 90 | ${SODIUM_LIBRARY} 91 | ) 92 | 93 | if (WIN32) 94 | target_link_libraries(pool_frontend_zcash ws2_32 mswsock) 95 | else() 96 | target_link_libraries(pool_frontend_zcash dl) 97 | endif() 98 | -------------------------------------------------------------------------------- /src/protocol.proto: -------------------------------------------------------------------------------- 1 | 2 | package pool.proto; 3 | 4 | 5 | 6 | message Block { 7 | 8 | required uint32 height = 1; 9 | required string hash = 2; 10 | required string prevhash = 3; 11 | required uint32 reqdiff = 4; 12 | required uint32 minshare = 5; 13 | 14 | } 15 | 16 | message Signal { 17 | 18 | enum Type { NEWBLOCK = 1; SHUTDOWN = 2; } 19 | 20 | required Type type = 1; 21 | 22 | optional Block block = 2; 23 | 24 | } 25 | 26 | 27 | 28 | message ClientStats { 29 | 30 | required string addr = 1; 31 | required string name = 2; 32 | required fixed64 clientid = 3; 33 | required fixed64 instanceid = 4; 34 | 35 | required uint32 version = 10; 36 | required float cpd = 11; 37 | required uint32 latency = 12; 38 | required uint32 temp = 13; 39 | required uint32 errors = 14; 40 | required uint32 ngpus = 15; 41 | required uint32 height = 16; 42 | optional uint32 unitType = 17; 43 | 44 | } 45 | 46 | message Share { 47 | 48 | required string addr = 1; 49 | required string name = 2; 50 | required fixed64 clientid = 3; 51 | optional uint32 gpuid = 4; 52 | 53 | required string hash = 10; 54 | required string merkle = 11; 55 | required uint32 time = 12; 56 | required uint32 bits = 13; 57 | required uint32 nonce = 14; 58 | required string multi = 15; 59 | optional string blockhash = 16; 60 | 61 | required uint32 height = 20; 62 | required uint32 length = 21; 63 | required uint32 chaintype = 22; 64 | required bool isblock = 23; 65 | optional uint64 genvalue = 24; 66 | 67 | optional string bigNonce = 25; 68 | optional bytes proofOfWork = 26; 69 | 70 | } 71 | 72 | message Request { 73 | 74 | enum Type { NONE = 0; CONNECT = 1; GETWORK = 2; SHARE = 3; STATS = 4; PING = 5; } 75 | 76 | required Type type = 1; 77 | required uint32 reqid = 2; 78 | 79 | optional uint32 version = 10; 80 | optional uint32 height = 11; 81 | optional bytes reqnonce = 12; 82 | 83 | optional Share share = 20; 84 | optional ClientStats stats = 21; 85 | 86 | } 87 | 88 | 89 | 90 | message ServerInfo { 91 | 92 | required string host = 1; 93 | required uint32 router = 2; 94 | required uint32 pub = 3; 95 | required uint32 target = 4; 96 | 97 | } 98 | 99 | message Work { 100 | 101 | // common 102 | required uint32 height = 1; 103 | required string merkle = 2; 104 | required uint32 time = 3; 105 | required uint32 bits = 4; 106 | 107 | // zcash specific 108 | optional string hashReserved = 5; 109 | optional uint32 n = 6; 110 | optional uint32 k = 7; 111 | 112 | } 113 | 114 | message Reply { 115 | 116 | enum ErrType { NONE = 0; VERSION = 1; HEIGHT = 2; REQNONCE = 3; STALE = 4; INVALID = 5; DUPLICATE = 6; } 117 | 118 | required Request.Type type = 1; 119 | required uint32 reqid = 2; 120 | 121 | required ErrType error = 10; 122 | optional string errstr = 11; 123 | 124 | optional ServerInfo sinfo = 20; 125 | optional Work work = 21; 126 | optional Block block = 22; 127 | 128 | } 129 | 130 | 131 | 132 | message ReqStats { 133 | 134 | required Request.Type reqtype = 1; 135 | required Reply.ErrType errtype = 2; 136 | required uint32 count = 3; 137 | 138 | } 139 | 140 | message ServerStats { 141 | 142 | required string name = 1; 143 | required uint32 thread = 2; 144 | 145 | required uint32 workers = 10; 146 | required uint32 latency = 11; 147 | required float cpd = 12; 148 | 149 | repeated ReqStats reqstats = 20; 150 | 151 | } 152 | 153 | message Data { 154 | 155 | optional Share share = 1; 156 | optional ClientStats clientstats = 2; 157 | optional ServerStats serverstats = 3; 158 | 159 | } 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /src/zcashpool.h: -------------------------------------------------------------------------------- 1 | #include "stratum.h" 2 | #include "protocol.pb.h" 3 | #include 4 | #include 5 | #include "uint256.h" 6 | #include "asyncio/asyncioTypes.h" 7 | #include "asyncio/device.h" 8 | 9 | struct asyncBase; 10 | struct aioObject; 11 | struct zmtpSocket; 12 | class p2pNode; 13 | class PoolBackend; 14 | 15 | struct StratumWorker { 16 | private: 17 | enum { 18 | AccumulateInterval = 60, 19 | Interval = 9, 20 | }; 21 | 22 | time_t begin; 23 | unsigned values[Interval]; 24 | 25 | void shift() { 26 | time_t currentTime = time(0)+AccumulateInterval; 27 | if (currentTime - begin >= AccumulateInterval) { 28 | unsigned shiftCount = (currentTime-begin)/AccumulateInterval; 29 | if (shiftCount < Interval) { 30 | for (unsigned i = 0; i < Interval-shiftCount; i++) 31 | values[Interval-i-1] = values[Interval-shiftCount-i-1]; 32 | for (unsigned i = 0; i < shiftCount; i++) 33 | values[i] = 0; 34 | } else { 35 | memset(values, 0, sizeof(values)); 36 | } 37 | 38 | begin = currentTime; 39 | } 40 | } 41 | 42 | public: 43 | std::string wallet; 44 | std::string worker; 45 | aioObject *socket; 46 | time_t lastUpdateTime; 47 | 48 | StratumWorker() { 49 | begin = time(0)+AccumulateInterval; 50 | memset(values, 0, sizeof(values)); 51 | lastUpdateTime = 0; 52 | } 53 | 54 | void pushShare() { 55 | shift(); 56 | values[0]++; 57 | } 58 | 59 | double updateStats() { 60 | unsigned sum = 0; 61 | for (unsigned i = 1; i < Interval; i++) 62 | sum += values[i]; 63 | return (double)sum / ((Interval-1)*AccumulateInterval); 64 | } 65 | }; 66 | 67 | struct StratumTask { 68 | unsigned bits; 69 | std::string hashReserved; 70 | std::string merkle; 71 | }; 72 | 73 | struct poolContext { 74 | asyncBase *base; 75 | pipeTy signalPipeFd; 76 | aioObject *signalWriteObject; 77 | aioObject *signalReadObject; 78 | aioObject *mainSocket; 79 | 80 | std::string xpmclientHost; 81 | uint16_t xpmclientListenPort; 82 | uint16_t xpmclientWorkPort; 83 | PoolBackend *backend; 84 | p2pNode *client; 85 | 86 | pool::proto::Block mCurrBlock; 87 | std::map extraNonceMap; 88 | std::map stratumTaskMap; 89 | std::set uniqueShares; 90 | double difficulty; 91 | uint256 blockTarget; 92 | 93 | unsigned shareTargetCoeff; 94 | uint256 shareTarget; 95 | mpz_class shareTargetMpz; 96 | uint32_t shareTargetBits; 97 | std::string shareTargetForStratum; 98 | 99 | bool equihashShareCheck; 100 | 101 | // stratum 102 | bool checkAddress; 103 | int64_t sessionId; 104 | unsigned stratumWorkLifeTime; 105 | 106 | // must be in thread context 107 | std::vector signalSockets; 108 | std::map stratumWorkers; 109 | }; 110 | 111 | struct readerContext { 112 | socketTy socket; 113 | poolContext *poolCtx; 114 | unsigned shareCounter; 115 | }; 116 | 117 | unsigned getMinShare(unsigned difficultyTarget); 118 | 119 | bool checkRequest(poolContext *context, 120 | pool::proto::Request &req, 121 | pool::proto::Reply &rep, 122 | void *msg, 123 | size_t msgSize); 124 | 125 | void onConnect(poolContext *context, 126 | pool::proto::Request &req, 127 | pool::proto::Reply &rep); 128 | 129 | void onGetWork(poolContext *context, 130 | pool::proto::Request &req, 131 | pool::proto::Reply &rep, 132 | bool *needDisconnect); 133 | 134 | void onShare(poolContext *context, 135 | pool::proto::Request &req, 136 | pool::proto::Reply &rep, 137 | bool *needDisconnect); 138 | 139 | void onStats(poolContext *context, 140 | pool::proto::Request &req, 141 | pool::proto::Reply &rep); 142 | 143 | 144 | 145 | // -- stratum callbacks -- 146 | 147 | void onStratumSubscribe(poolContext *context, aioObject *socket, StratumMessage *subscribe, int64_t sessionId); 148 | void onStratumAuthorize(poolContext *context, aioObject *socket, StratumMessage *msg, int64_t sessionId); 149 | void onStratumSubmit(poolContext *context, aioObject *socket, StratumMessage *msg, int64_t sessionId); 150 | 151 | void stratumSendSetTarget(poolContext *context, aioObject *socket); 152 | bool stratumSendNewWork(poolContext *context, aioObject *socket, int64_t sessionId); 153 | void stratumSendStats(poolContext *context, StratumWorker &worker); 154 | -------------------------------------------------------------------------------- /src/stratum.cpp: -------------------------------------------------------------------------------- 1 | #include "stratum.h" 2 | #include "jansson.h" 3 | 4 | static const char *decodeAsCString(json_t *in) 5 | { 6 | if (json_is_string(in)) { 7 | return json_string_value(in); 8 | } else if (json_is_null(in)) { 9 | return ""; 10 | } 11 | 12 | return 0; 13 | } 14 | 15 | static bool decodeAsString(json_t *in, std::string &out) 16 | { 17 | if (json_is_string(in)) { 18 | out = json_string_value(in); 19 | return true; 20 | } else if (json_is_null(in)) { 21 | out.clear(); 22 | return true; 23 | } 24 | 25 | return false; 26 | } 27 | 28 | static bool decodeAsInteger(json_t *in, int64_t *out) 29 | { 30 | if (json_is_integer(in)) { 31 | *out = json_integer_value(in); 32 | return true; 33 | } else if (json_is_string(in)) { 34 | const char *s = json_string_value(in); 35 | if (s[0] >= '0' && s[0] <= '9') { 36 | *out = xatoi(s); 37 | return true; 38 | } 39 | } 40 | 41 | return false; 42 | } 43 | 44 | static bool readHex(const char *in, uint8_t *out, size_t outSizeInBytes) 45 | { 46 | const char *p = in; 47 | uint8_t *o = out; 48 | int state = 0; 49 | size_t offset = 0; 50 | uint8_t X = 0; 51 | while (*p) { 52 | if (*p >= '0' && *p <= '9') 53 | X |= (*p - '0'); 54 | else if (*p >= 'A' && *p <= 'F') 55 | X |= (*p - 'A' + 10); 56 | else if (*p >= 'a' && *p <= 'f') 57 | X |= (*p - 'a' + 10); 58 | else 59 | return false; 60 | 61 | state ^= 1; 62 | if (state == 0) { 63 | *o++ = X; 64 | X = 0; 65 | } else { 66 | X <<= 4; 67 | } 68 | 69 | p++; 70 | } 71 | 72 | return state == 0; 73 | } 74 | 75 | static bool readHex(const char *in, std::vector &out) 76 | { 77 | out.clear(); 78 | const char *p = in; 79 | int state = 0; 80 | size_t offset = 0; 81 | uint8_t X = 0; 82 | while (*p) { 83 | if (*p >= '0' && *p <= '9') 84 | X |= (*p - '0'); 85 | else if (*p >= 'A' && *p <= 'F') 86 | X |= (*p - 'A' + 10); 87 | else if (*p >= 'a' && *p <= 'f') 88 | X |= (*p - 'a' + 10); 89 | else 90 | return false; 91 | 92 | state ^= 1; 93 | if (state == 0) { 94 | out.push_back(X); 95 | X = 0; 96 | } else { 97 | X <<= 4; 98 | } 99 | 100 | p++; 101 | } 102 | 103 | return state == 0; 104 | } 105 | 106 | StratumDecodeStatusTy decodeStratumMessage(const char *in, StratumMessage *out) 107 | { 108 | json_error_t jsonError; 109 | json_t *r = json_loads(in, 0, &jsonError); 110 | if (!r) 111 | return StratumDecodeStatusTy::JsonError; 112 | 113 | json_t *id = json_object_get(r, "id"); 114 | json_t *method = json_object_get(r, "method"); 115 | json_t *params = json_object_get(r, "params"); 116 | 117 | if (id && json_is_integer(id)) { 118 | out->id = json_integer_value(id); 119 | } else { 120 | json_delete(r); 121 | return StratumDecodeStatusTy::FormatError; 122 | } 123 | 124 | const char *methodName; 125 | if (method && json_is_string(method)) { 126 | methodName = json_string_value(method); 127 | } else { 128 | json_delete(r); 129 | return StratumDecodeStatusTy::FormatError; 130 | } 131 | 132 | if (!(params && json_is_array(params))) { 133 | json_delete(r); 134 | return StratumDecodeStatusTy::FormatError; 135 | } 136 | 137 | const char *time; 138 | const char *nonce; 139 | const char *equihashSolution; 140 | if (strcmp(methodName, "mining.subscribe") == 0 && json_array_size(params) >= 4) { 141 | out->method = StratumMethodTy::Subscribe; 142 | if ( !(decodeAsString(json_array_get(params, 0), out->subscribe.minerUserAgent) && 143 | decodeAsString(json_array_get(params, 1), out->subscribe.sessionId) && 144 | decodeAsString(json_array_get(params, 2), out->subscribe.connectHost) && 145 | decodeAsInteger(json_array_get(params, 3), &out->subscribe.connectPort)) ) 146 | return StratumDecodeStatusTy::FormatError; 147 | } else if (strcmp(methodName, "mining.authorize") == 0 && json_array_size(params) >= 1) { 148 | out->method = StratumMethodTy::Authorize; 149 | if ( !(decodeAsString(json_array_get(params, 0), out->authorize.login)) ) 150 | return StratumDecodeStatusTy::FormatError; 151 | } else if (strcmp(methodName, "mining.extranonce.subscribe") == 0) { 152 | out->method = StratumMethodTy::ExtraNonceSubscribe; 153 | } else if (strcmp(methodName, "mining.submit") == 0 && json_array_size(params) >= 5) { 154 | if ( !(decodeAsString(json_array_get(params, 0), out->submit.workerName) && 155 | decodeAsString(json_array_get(params, 1), out->submit.jobId) && 156 | (time = decodeAsCString(json_array_get(params, 2))) && 157 | (nonce = decodeAsCString(json_array_get(params, 3))) && 158 | (equihashSolution = decodeAsCString(json_array_get(params, 4)))) ) 159 | return StratumDecodeStatusTy::FormatError; 160 | if (!readHex(time, (uint8_t*)&out->submit.time, 4) || 161 | !readHex(nonce, out->submit.nonce, 28) || 162 | !readHex(equihashSolution, out->submit.equihashSolution)) 163 | return StratumDecodeStatusTy::FormatError; 164 | out->method = StratumMethodTy::Submit; 165 | } else { 166 | json_delete(r); 167 | return StratumDecodeStatusTy::FormatError; 168 | } 169 | 170 | json_delete(r); 171 | return StratumDecodeStatusTy::Ok; 172 | } 173 | 174 | void stratumLittleEndianHex(const char *in, size_t inSize, char *out) 175 | { 176 | for (size_t i = 0; i < inSize/2; i++) { 177 | out[i*2] = in[inSize-i*2-2]; 178 | out[i*2+1] = in[inSize-i*2-1]; 179 | } 180 | 181 | out[inSize] = 0; 182 | } 183 | -------------------------------------------------------------------------------- /src/equihash_original.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Jack Grigg 2 | // Copyright (c) 2016 The Zcash developers 3 | // Distributed under the MIT software license, see the accompanying 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | #ifndef BITCOIN_EQUIHASH_H 7 | #define BITCOIN_EQUIHASH_H 8 | 9 | // #include "sha256.h" 10 | #include 11 | 12 | #include "sodium.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | //#include 24 | 25 | typedef crypto_generichash_blake2b_state eh_HashState; 26 | typedef uint32_t eh_index; 27 | typedef uint8_t eh_trunc; 28 | 29 | static inline int LogPrint(const char *category, const char *format, ...) 30 | { 31 | // va_list arguments; 32 | // va_start(arguments, format); 33 | // vfprintf(stderr, format, arguments); 34 | } 35 | 36 | template 37 | std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) 38 | { 39 | std::string rv; 40 | static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', 41 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 42 | rv.reserve((itend-itbegin)*3); 43 | for(T it = itbegin; it < itend; ++it) 44 | { 45 | unsigned char val = (unsigned char)(*it); 46 | if(fSpaces && it != itbegin) 47 | rv.push_back(' '); 48 | rv.push_back(hexmap[val>>4]); 49 | rv.push_back(hexmap[val&15]); 50 | } 51 | 52 | return rv; 53 | } 54 | 55 | void ExpandArray(const unsigned char* in, size_t in_len, 56 | unsigned char* out, size_t out_len, 57 | size_t bit_len, size_t byte_pad=0); 58 | void CompressArray(const unsigned char* in, size_t in_len, 59 | unsigned char* out, size_t out_len, 60 | size_t bit_len, size_t byte_pad=0); 61 | 62 | eh_index ArrayToEhIndex(const unsigned char* array); 63 | eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen); 64 | 65 | std::vector GetIndicesFromMinimal(std::vector minimal, 66 | size_t cBitLen); 67 | std::vector GetMinimalFromIndices(std::vector indices, 68 | size_t cBitLen); 69 | 70 | template 71 | class StepRow 72 | { 73 | template 74 | friend class StepRow; 75 | friend class CompareSR; 76 | 77 | protected: 78 | unsigned char hash[WIDTH]; 79 | 80 | public: 81 | StepRow(const unsigned char* hashIn, size_t hInLen, 82 | size_t hLen, size_t cBitLen); 83 | ~StepRow() { } 84 | 85 | template 86 | StepRow(const StepRow& a); 87 | 88 | bool IsZero(size_t len); 89 | std::string GetHex(size_t len) { return HexStr(hash, hash+len); } 90 | 91 | template 92 | friend bool HasCollision(StepRow& a, StepRow& b, int l); 93 | }; 94 | 95 | class CompareSR 96 | { 97 | private: 98 | size_t len; 99 | 100 | public: 101 | CompareSR(size_t l) : len {l} { } 102 | 103 | template 104 | inline bool operator()(const StepRow& a, const StepRow& b) { return memcmp(a.hash, b.hash, len) < 0; } 105 | }; 106 | 107 | template 108 | bool HasCollision(StepRow& a, StepRow& b, int l); 109 | 110 | template 111 | class FullStepRow : public StepRow 112 | { 113 | template 114 | friend class FullStepRow; 115 | 116 | using StepRow::hash; 117 | 118 | public: 119 | FullStepRow(const unsigned char* hashIn, size_t hInLen, 120 | size_t hLen, size_t cBitLen, eh_index i); 121 | ~FullStepRow() { } 122 | 123 | FullStepRow(const FullStepRow& a) : StepRow {a} { } 124 | template 125 | FullStepRow(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices, int trim); 126 | FullStepRow& operator=(const FullStepRow& a); 127 | 128 | inline bool IndicesBefore(const FullStepRow& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; } 129 | std::vector GetIndices(size_t len, size_t lenIndices, 130 | size_t cBitLen) const; 131 | 132 | template 133 | friend bool DistinctIndices(const FullStepRow& a, const FullStepRow& b, 134 | size_t len, size_t lenIndices); 135 | template 136 | friend bool IsValidBranch(const FullStepRow& a, const size_t len, const unsigned int ilen, const eh_trunc t); 137 | }; 138 | 139 | template 140 | class TruncatedStepRow : public StepRow 141 | { 142 | template 143 | friend class TruncatedStepRow; 144 | 145 | using StepRow::hash; 146 | 147 | public: 148 | TruncatedStepRow(const unsigned char* hashIn, size_t hInLen, 149 | size_t hLen, size_t cBitLen, 150 | eh_index i, unsigned int ilen); 151 | ~TruncatedStepRow() { } 152 | 153 | TruncatedStepRow(const TruncatedStepRow& a) : StepRow {a} { } 154 | template 155 | TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, size_t len, size_t lenIndices, int trim); 156 | TruncatedStepRow& operator=(const TruncatedStepRow& a); 157 | 158 | inline bool IndicesBefore(const TruncatedStepRow& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; } 159 | std::shared_ptr GetTruncatedIndices(size_t len, size_t lenIndices) const; 160 | }; 161 | 162 | enum EhSolverCancelCheck 163 | { 164 | ListGeneration, 165 | ListSorting, 166 | ListColliding, 167 | RoundEnd, 168 | FinalSorting, 169 | FinalColliding, 170 | PartialGeneration, 171 | PartialSorting, 172 | PartialSubtreeEnd, 173 | PartialIndexEnd, 174 | PartialEnd 175 | }; 176 | 177 | class EhSolverCancelledException : public std::exception 178 | { 179 | virtual const char* what() const throw() { 180 | return "Equihash solver was cancelled"; 181 | } 182 | }; 183 | 184 | inline constexpr const size_t max(const size_t A, const size_t B) { return A > B ? A : B; } 185 | 186 | inline constexpr size_t equihash_solution_size(unsigned int N, unsigned int K) { 187 | return (1 << K)*(N/(K+1)+1)/8; 188 | } 189 | 190 | template 191 | class Equihash 192 | { 193 | public: 194 | enum : size_t { IndicesPerHashOutput=512/N }; 195 | enum : size_t { HashOutput=IndicesPerHashOutput*N/8 }; 196 | enum : size_t { CollisionBitLength=N/(K+1) }; 197 | enum : size_t { CollisionByteLength=(CollisionBitLength+7)/8 }; 198 | enum : size_t { HashLength=(K+1)*CollisionByteLength }; 199 | enum : size_t { FullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K-1)) }; 200 | enum : size_t { FinalFullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K)) }; 201 | enum : size_t { TruncatedWidth=max(HashLength+sizeof(eh_trunc), 2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K-1))) }; 202 | enum : size_t { FinalTruncatedWidth=max(HashLength+sizeof(eh_trunc), 2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K))) }; 203 | enum : size_t { SolutionWidth=(1 << K)*(CollisionBitLength+1)/8 }; 204 | 205 | Equihash() { } 206 | 207 | int InitialiseState(eh_HashState& base_state); 208 | bool BasicSolve(const eh_HashState& base_state, 209 | const std::function)> validBlock, 210 | const std::function cancelled); 211 | bool OptimisedSolve(const eh_HashState& base_state, 212 | const std::function)> validBlock, 213 | const std::function cancelled); 214 | bool IsValidSolution(const eh_HashState& base_state, std::vector &soln); 215 | }; 216 | 217 | #include "equihash.tcc" 218 | 219 | static Equihash<96,3> Eh96_3; 220 | static Equihash<200,9> Eh200_9; 221 | static Equihash<96,5> Eh96_5; 222 | static Equihash<48,5> Eh48_5; 223 | 224 | 225 | #endif // BITCOIN_EQUIHASH_H 226 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###### Description 2 | 3 | This is ZCash mining pool. 4 | 5 | * poolcore - common pool library for accounting and statistics handling 6 | * pool_frontend_zcash - frontend, support native ZMQ and stratum protocols 7 | * poolrestapi - standalone module for REST api support 8 | * ngxrest - nginx module for REST api, fastcgi analogue 9 | 10 | ###### Install dependencies 11 | 12 | sudo apt-get install cmake libssl-dev libsodium-dev libpcre3-dev libleveldb-dev libboost-all-dev libgmp-dev libprotobuf-dev protobuf-compiler libjansson-dev 13 | 14 | ###### Download sources 15 | 16 | ``` 17 | cd YOUR_BUILD_DIRECTORY 18 | git clone https://github.com/zcash/zcash 19 | git clone https://github.com/config4star/config4cpp 20 | git clone https://github.com/google/flatbuffers 21 | git clone https://github.com/eXtremal-ik7/libp2p -b version/0.4 22 | git clone https://github.com/eXtremal-ik7/poolcore 23 | git clone https://github.com/eXtremal-ik7/pool_frontend_zcash 24 | git clone https://github.com/eXtremal-ik7/poolrestapi 25 | git clone https://github.com/eXtremal-ik7/ngxrest 26 | git clone https://github.com/eXtremal-ik7/pooljs 27 | wget https://nginx.org/download/nginx-1.15.8.tar.gz 28 | tar -xzf nginx-1.15.8.tar.gz 29 | ``` 30 | 31 | ###### Build pool sources 32 | ``` 33 | cd YOUR_BUILD_DIRECTORY/config4cpp 34 | make -j5 35 | 36 | cd YOUR_BUILD_DIRECTORY/flatbuffers 37 | mkdir build 38 | cd build 39 | cmake .. 40 | make -j5 41 | sudo make install 42 | 43 | cd YOUR_BUILD_DIRECTORY/libp2p 44 | mkdir x86_64-Linux 45 | cd x86_64-Linux 46 | cmake ../src 47 | make -j5 48 | 49 | cd YOUR_BUILD_DIRECTORY/poolcore 50 | mkdir x86_64-Linux 51 | cd x86_64-Linux 52 | cmake ../src -DROOT_SOURCE_DIR=YOUR_BUILD_DIRECTORY -DZCASH_ENABLED=1 53 | make -j5 54 | 55 | cd YOUR_BUILD_DIRECTORY/pool_frontend_zcash 56 | mkdir x86_64-Linux 57 | cd x86_64-Linux 58 | cmake ../src -DROOT_SOURCE_DIR=YOUR_BUILD_DIRECTORY 59 | make -j5 60 | 61 | cd YOUR_BUILD_DIRECTORY/poolrestapi 62 | mkdir x86_64-Linux 63 | cd x86_64-Linux 64 | cmake ../src -DROOT_SOURCE_DIR=YOUR_BUILD_DIRECTORY 65 | make -j5 66 | 67 | cd YOUR_BUILD_DIRECTORY/nginx-1.15.8 68 | ./configure --prefix=NGINX_INSTALL_DIRECTORY --add-module=YOUR_BUILD_DIRECTORY/ngxrest 69 | make -j5 70 | make install 71 | ``` 72 | 73 | ###### Setup ZCash daemon (version 2.0.3 or higher required) 74 | 75 | You can find setup instructions on ZCash github page: https://zcash.readthedocs.io/en/latest/rtd_pages/user_guide.html 76 | 77 | After building by command ./zcutil/build.sh you must apply pool integration patch: 78 | 79 | ``` 80 | patch -p0 < pool.diff 81 | ``` 82 | 83 | 84 | Add path to pool libraries in Makefile: 85 | 86 | ``` 87 | --- src/Makefile 2016-11-08 13:36:41.033699973 +0300 88 | +++ src/Makefile 2016-11-08 13:37:20.889665298 +0300 89 | @@ -1380,7 +1380,7 @@ 90 | LIBSNARK_LIBS = -lsnark 91 | LIBTOOL = $(SHELL) $(top_builddir)/libtool 92 | LIBTOOL_APP_LDFLAGS = 93 | -LIBZCASH_LIBS = -lsnark -lgmp -lgmpxx -lboost_system-mt -lcrypto -lsodium -fopenmp 94 | +LIBZCASH_LIBS = -lsnark -lgmp -lgmpxx -lboost_system-mt -lcrypto -lsodium -fopenmp -LYOUR_BUILD_DIRECTORY/poolcore/x86_64-Linux/zcash -lpoolrpczcash -LYOUR_BUILD_DIRECTORY/libp2p/x86_64-Linux/p2p -lp2p -LYOUR_BUILD_DIRECTORY/libp2p/x86_64-Linux/asyncio -lasyncio-0.4 -lrt 95 | LIPO = 96 | LN_S = ln -s 97 | LRELEASE = 98 | ``` 99 | 100 | Build ZCash daemon again: 101 | ``` 102 | make -j5 103 | ``` 104 | 105 | Launch command is src/zcashd -p2pport=12201. Port 12201 is used by internal pool protocol. Also, you can use poolrpccmd utility for interact with daemon by command line. 106 | 107 | ###### Setup pool configuration file 108 | ``` 109 | pool_frontend_zcash { 110 | isMaster = "true"; 111 | poolFee = "1"; 112 | poolFeeAddr = "t1ZsLopJKzyuaqmeC2Y6cx1351G13st2sWN"; 113 | walletAddrs = ["p2p://127.0.0.1:12201"]; 114 | localAddress = "p2p://127.0.0.1:13301"; 115 | walletAppName = "pool_rpc"; 116 | poolAppName = "pool_frontend_zcash"; 117 | requiredConfirmations = "10"; 118 | defaultMinimalPayout = "0.01"; 119 | minimalPayout = "0.001"; 120 | dbPath = "/home/xpm/pool.zcash"; 121 | keepRoundTime = "3"; 122 | keepStatsTime = "2"; 123 | confirmationsCheckInterval = "7"; 124 | payoutInterval = "30"; 125 | balanceCheckInterval = "3"; 126 | statisticCheckInterval = "1"; 127 | checkAddress = "true"; 128 | shareTarget = "2048"; 129 | equihashShareCheck = "true"; 130 | stratumWorkLifeTime = "9"; 131 | 132 | zmqclientHost = "coinsforall.io"; 133 | zmqclientListenPort = "6668"; 134 | zmqclientWorkPort = "60200"; 135 | 136 | pool_zaddr = "zcWDMjkEyRPQAFjs5RWEzoqXG9BipLRWr4S9bC4A44g7eBv6ZhnqrWWc5M5MyNn4pt9vP2PEvK9NkUwerQbewEcEWz8ZL4f"; 137 | pool_taddr = "t1ZRsyK4pkBq7WD4gnF8r3nCYSQT19Zhh3C"; 138 | } 139 | ``` 140 | 141 | * isMaster - alltimes "true", slave mode for distribute share calculating not implemented 142 | * poolFee - pool fee in percents 143 | * poolFeeAddr - address for pool fee 144 | * walletAddrs - list of zcash daemons in pool cluster. Now tested only with one daemon. Port 12201 must be taken from -p2pport command line argument of zcash daemon (see 4). 145 | * localAddress - backend address and port. Used by poolrestapi module and poolrpccmd utility 146 | * walletAppName - alltimer "pool_rpc" 147 | * poolAppName - alltimes "pool_frontend_zcash" 148 | * requiredConfirmations - minimal confirmations for block before payout (pool can't make payout for orphans) 149 | * defaultMinimalPayout - default minimal payout for new accounts 150 | * minimalPayout - minimal allowed payout in one transaction 151 | * dbPath - path for pool database, must be exists 152 | * keepRoundTime - how much days pool keep shares 153 | * keepStatsTime - statistic (performance, number of GPUs, temperatures) keep time in minutes 154 | * confirmationsCheckInterval - interval for check new mined blocks confirmations number 155 | * payoutInterval - payout interval in minutes 156 | * balanceCheckInterval - balance check interval in minutes 157 | * statisticCheckInterval - statistic check interval in minutes (should be 1) 158 | * checkAddress - enable client address checking. Use 'false' when you setup pool on testnet 159 | * shareTarget - fixed share difficulty for native(zeromq) and stratum miners 160 | * equihashShareCheck - enable additional equihash share checking, not need on private pools. Disable it for less CPU usage 161 | * stratumWorkLifeTime - stratum work update interval in minutes, required for some miners 162 | 163 | * zmqclientHost - native zeromq protocol host name 164 | * zmqclientListenPort - navite protocol port 165 | * zmqclientWorkPort - extra port for native protocol (pool uses port N and N+1) 166 | 167 | * pool_zaddr - only for ZCash, pool Z-Address for receive coinbase funds. Must be created by zcash-cli z_getnewaddress 168 | * pool_taddr - only for ZCash, address used for make payouts. Must be created by zcash-cli getnewaddress 169 | 170 | ###### Setup poolrestapi module 171 | ``` 172 | poolrestapi { 173 | listenAddress = "cxxrestapi://127.0.0.1:19999"; 174 | coins = ["xpm", "zcash"]; 175 | } 176 | 177 | xpm { 178 | frontends = ["p2p://127.0.0.1:13300"]; 179 | poolAppName = "pool_frontend_xpm"; 180 | } 181 | 182 | zcash { 183 | frontends = ["p2p://127.0.0.1:13301"]; 184 | poolAppName = "pool_frontend_zcash"; 185 | } 186 | ``` 187 | 188 | * listenAddress - address and port for poolrestapi. Used by nginx module for fast interaction (like fastcgi). 189 | * coins - coin list. Name used as URL part for HTTP REST queries. 190 | 191 | * frontends - pool frontend addresses, must be taken from 'localAddress' of pool frontend configuration file 192 | * poolAppName - must be taken from 'poolAppName' of pool frontend configuration file 193 | 194 | ###### Setup nginx 195 | 196 | This is fragment of nginx.conf file: 197 | ``` 198 | upstream api_backend { 199 | server 127.0.0.1:19999; 200 | keepalive 32; 201 | } 202 | 203 | server { 204 | listen *:80 reuseport; 205 | server_name pool; 206 | 207 | location / { 208 | root html; 209 | index index.html index.htm; 210 | expires 5s; 211 | } 212 | 213 | location /api { 214 | cxxrest_pass api_backend; 215 | } 216 | 217 | location ~* ^.+\.(jpg|jpeg|gif|png|ico)$ { 218 | expires 3d; # 3 days 219 | } 220 | } 221 | ``` 222 | 223 | * upstream api_backend: 224 | * server - poolrestapi address, must be taken from poolrestapi configuration file (see p.6) 225 | 226 | * server/location /api: 227 | * cxxrest_pass - module name and link to upstream api_backend 228 | 229 | Full nginx configuration file example you can find in poolrestapi repository 230 | 231 | ###### Launch ZCash daemon 232 | ``` 233 | src/zcashd -p2pport=12201 234 | ``` 235 | 236 | After launching you can test it with poolrpccmd utility (from poolcore repository) 237 | 238 | ``` 239 | cd YOUR_BUILD_DIRECTORY/poolcore/x8664-Linux 240 | poolrpccmd/poolrpccmd p2p://127.0.0.1:12201 getInfo 241 | ``` 242 | 243 | ``` 244 | * coin: ZEC 245 | ``` 246 | 247 | ``` 248 | poolrpccmd/poolrpccmd p2p://127.0.0.1:12201 getBlockTemplate 249 | ``` 250 | 251 | ``` 252 | getBlockTemplate call duration 0.307ms 253 | * nbits: 486813521 254 | * prevhash: 0000000122760d5a6e162e08e5b7f94b9d631081bc50cafd2e9e402c405eebed 255 | * hashreserved: 0000000000000000000000000000000000000000000000000000000000000000 256 | * merkle: d803756fa90508f6e94d274236ebd158b35d79329997dbef6fa576d8830f5b58 257 | * time: 1478536889 258 | * extraNonce: 462 259 | * equilHashK = 9 260 | * equilHashN = 200 261 | ``` 262 | ###### Launch pool 263 | 264 | ``` 265 | cd YOUR_BUILD_DIRECTORY/pool_frontend_zcash/x86_64-Linux 266 | ./pool_frontend_zcash ~/.poolcfg/zcash.cfg 267 | ``` 268 | 269 | ~/.poolcfg/zcash.cfg - pool configuration file (see p. 4) 270 | 271 | ###### Launch poolrestapi and nginx 272 | 273 | ``` 274 | cd YOUR_BUILD_DIRECTORY/poolrestapi/x86_64-Linux 275 | ./poolrestapi ~/.poolcfg/poolrestapi.cfg 276 | ``` 277 | 278 | ###### Copy web client part and launch nginx 279 | 280 | ``` 281 | cd YOUR_BUILD_DIRECTORY/pooljs/coins-for-all/webapp 282 | cp -r * NGINX_INSTALL_DIRECTORY/html 283 | sudo NGINX_INSTALL_DIRECTORY/sbin/nginx (you need sudo because port 80 is used) 284 | ``` 285 | 286 | -------------------------------------------------------------------------------- /src/equihash_original.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Jack Grigg 2 | // Copyright (c) 2016 The Zcash developers 3 | // Distributed under the MIT software license, see the accompanying 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | 6 | // Implementation of the Equihash Proof-of-Work algorithm. 7 | // 8 | // Reference 9 | // ========= 10 | // Alex Biryukov and Dmitry Khovratovich 11 | // Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem 12 | // NDSS ’16, 21-24 February 2016, San Diego, CA, USA 13 | // https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf 14 | 15 | #include "equihash_original.h" 16 | // #include "util.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include "loguru.hpp" 22 | 23 | 24 | 25 | #if defined(_WIN32) 26 | #include 27 | # define htobe32(x) __builtin_bswap32 (x) 28 | # define htole32(x) (x) 29 | # define be32toh(x) __builtin_bswap32 (x) 30 | # define le32toh(x) (x) 31 | 32 | extern "C" 33 | void sodium_memzero(void *ptr, size_t cnt) { memset(ptr, 0, cnt); } 34 | #elif defined(__APPLE__) 35 | # define htobe32(x) __builtin_bswap32 (x) 36 | # define htole32(x) (x) 37 | # define be32toh(x) __builtin_bswap32 (x) 38 | # define le32toh(x) (x) 39 | #endif 40 | 41 | EhSolverCancelledException solver_cancelled; 42 | 43 | template 44 | int Equihash::InitialiseState(eh_HashState& base_state) 45 | { 46 | uint32_t le_N = htole32(N); 47 | uint32_t le_K = htole32(K); 48 | unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {}; 49 | memcpy(personalization, "ZcashPoW", 8); 50 | memcpy(personalization+8, &le_N, 4); 51 | memcpy(personalization+12, &le_K, 4); 52 | return crypto_generichash_blake2b_init_salt_personal(&base_state, 53 | NULL, 0, // No key. 54 | (512/N)*N/8, 55 | NULL, // No salt. 56 | personalization); 57 | } 58 | 59 | void GenerateHash(const eh_HashState& base_state, eh_index g, 60 | unsigned char* hash, size_t hLen) 61 | { 62 | eh_HashState state; 63 | state = base_state; 64 | eh_index lei = htole32(g); 65 | crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei, 66 | sizeof(eh_index)); 67 | crypto_generichash_blake2b_final(&state, hash, hLen); 68 | } 69 | 70 | void ExpandArray(const unsigned char* in, size_t in_len, 71 | unsigned char* out, size_t out_len, 72 | size_t bit_len, size_t byte_pad) 73 | { 74 | assert(bit_len >= 8); 75 | assert(8*sizeof(uint32_t) >= 7+bit_len); 76 | 77 | size_t out_width { (bit_len+7)/8 + byte_pad }; 78 | assert(out_len == 8*out_width*in_len/bit_len); 79 | 80 | uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 }; 81 | 82 | // The acc_bits least-significant bits of acc_value represent a bit sequence 83 | // in big-endian order. 84 | size_t acc_bits = 0; 85 | uint32_t acc_value = 0; 86 | 87 | size_t j = 0; 88 | for (size_t i = 0; i < in_len; i++) { 89 | acc_value = (acc_value << 8) | in[i]; 90 | acc_bits += 8; 91 | 92 | // When we have bit_len or more bits in the accumulator, write the next 93 | // output element. 94 | if (acc_bits >= bit_len) { 95 | acc_bits -= bit_len; 96 | for (size_t x = 0; x < byte_pad; x++) { 97 | out[j+x] = 0; 98 | } 99 | for (size_t x = byte_pad; x < out_width; x++) { 100 | out[j+x] = ( 101 | // Big-endian 102 | acc_value >> (acc_bits+(8*(out_width-x-1))) 103 | ) & ( 104 | // Apply bit_len_mask across byte boundaries 105 | (bit_len_mask >> (8*(out_width-x-1))) & 0xFF 106 | ); 107 | } 108 | j += out_width; 109 | } 110 | } 111 | } 112 | 113 | void CompressArray(const unsigned char* in, size_t in_len, 114 | unsigned char* out, size_t out_len, 115 | size_t bit_len, size_t byte_pad) 116 | { 117 | assert(bit_len >= 8); 118 | assert(8*sizeof(uint32_t) >= 7+bit_len); 119 | 120 | size_t in_width { (bit_len+7)/8 + byte_pad }; 121 | assert(out_len == bit_len*in_len/(8*in_width)); 122 | 123 | uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 }; 124 | 125 | // The acc_bits least-significant bits of acc_value represent a bit sequence 126 | // in big-endian order. 127 | size_t acc_bits = 0; 128 | uint32_t acc_value = 0; 129 | 130 | size_t j = 0; 131 | for (size_t i = 0; i < out_len; i++) { 132 | // When we have fewer than 8 bits left in the accumulator, read the next 133 | // input element. 134 | if (acc_bits < 8) { 135 | acc_value = acc_value << bit_len; 136 | for (size_t x = byte_pad; x < in_width; x++) { 137 | acc_value = acc_value | ( 138 | ( 139 | // Apply bit_len_mask across byte boundaries 140 | in[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF) 141 | ) << (8*(in_width-x-1))); // Big-endian 142 | } 143 | j += in_width; 144 | acc_bits += bit_len; 145 | } 146 | 147 | acc_bits -= 8; 148 | out[i] = (acc_value >> acc_bits) & 0xFF; 149 | } 150 | } 151 | 152 | // Big-endian so that lexicographic array comparison is equivalent to integer 153 | // comparison 154 | void EhIndexToArray(const eh_index i, unsigned char* array) 155 | { 156 | eh_index bei = htobe32(i); 157 | memcpy(array, &bei, sizeof(eh_index)); 158 | } 159 | 160 | // Big-endian so that lexicographic array comparison is equivalent to integer 161 | // comparison 162 | eh_index ArrayToEhIndex(const unsigned char* array) 163 | { 164 | eh_index bei; 165 | memcpy(&bei, array, sizeof(eh_index)); 166 | return be32toh(bei); 167 | } 168 | 169 | eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen) 170 | { 171 | // Truncate to 8 bits 172 | return (i >> (ilen - 8)) & 0xff; 173 | } 174 | 175 | eh_index UntruncateIndex(const eh_trunc t, const eh_index r, const unsigned int ilen) 176 | { 177 | eh_index i{t}; 178 | return (i << (ilen - 8)) | r; 179 | } 180 | 181 | std::vector GetIndicesFromMinimal(std::vector minimal, 182 | size_t cBitLen) 183 | { 184 | assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); 185 | size_t lenIndices { 8*sizeof(eh_index)*minimal.size()/(cBitLen+1) }; 186 | size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 }; 187 | std::vector array(lenIndices); 188 | ExpandArray(minimal.data(), minimal.size(), 189 | array.data(), lenIndices, cBitLen+1, bytePad); 190 | std::vector ret; 191 | for (int i = 0; i < lenIndices; i += sizeof(eh_index)) { 192 | ret.push_back(ArrayToEhIndex(array.data()+i)); 193 | } 194 | return ret; 195 | } 196 | 197 | std::vector GetMinimalFromIndices(std::vector indices, 198 | size_t cBitLen) 199 | { 200 | assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); 201 | size_t lenIndices { indices.size()*sizeof(eh_index) }; 202 | size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) }; 203 | size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 }; 204 | std::vector array(lenIndices); 205 | for (int i = 0; i < indices.size(); i++) { 206 | EhIndexToArray(indices[i], array.data()+(i*sizeof(eh_index))); 207 | } 208 | std::vector ret(minLen); 209 | 210 | CompressArray(array.data(), lenIndices, 211 | ret.data(), minLen, cBitLen+1, bytePad); 212 | return ret; 213 | } 214 | 215 | template 216 | StepRow::StepRow(const unsigned char* hashIn, size_t hInLen, 217 | size_t hLen, size_t cBitLen) 218 | { 219 | assert(hLen <= WIDTH); 220 | ExpandArray(hashIn, hInLen, hash, hLen, cBitLen); 221 | } 222 | 223 | template template 224 | StepRow::StepRow(const StepRow& a) 225 | { 226 | std::copy(a.hash, a.hash+W, hash); 227 | } 228 | 229 | template 230 | FullStepRow::FullStepRow(const unsigned char* hashIn, size_t hInLen, 231 | size_t hLen, size_t cBitLen, eh_index i) : 232 | StepRow {hashIn, hInLen, hLen, cBitLen} 233 | { 234 | EhIndexToArray(i, hash+hLen); 235 | } 236 | 237 | template template 238 | FullStepRow::FullStepRow(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices, int trim) : 239 | StepRow {a} 240 | { 241 | assert(len+lenIndices <= W); 242 | assert(len-trim+(2*lenIndices) <= WIDTH); 243 | for (int i = trim; i < len; i++) 244 | hash[i-trim] = a.hash[i] ^ b.hash[i]; 245 | if (a.IndicesBefore(b, len, lenIndices)) { 246 | std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim); 247 | std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices); 248 | } else { 249 | std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim); 250 | std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices); 251 | } 252 | } 253 | 254 | template 255 | FullStepRow& FullStepRow::operator=(const FullStepRow& a) 256 | { 257 | std::copy(a.hash, a.hash+WIDTH, hash); 258 | return *this; 259 | } 260 | 261 | template 262 | bool StepRow::IsZero(size_t len) 263 | { 264 | // This doesn't need to be constant time. 265 | for (int i = 0; i < len; i++) { 266 | if (hash[i] != 0) 267 | return false; 268 | } 269 | return true; 270 | } 271 | 272 | 273 | template 274 | bool HasCollision(StepRow& a, StepRow& b, int l) 275 | { 276 | // This doesn't need to be constant time. 277 | for (int j = 0; j < l; j++) { 278 | if (a.hash[j] != b.hash[j]) 279 | return false; 280 | } 281 | return true; 282 | } 283 | 284 | template 285 | bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector &soln) 286 | { 287 | if (soln.size() != SolutionWidth) { 288 | LOG_F(WARNING, "Invalid solution length: %d (expected %d)", (unsigned)soln.size(), (unsigned)SolutionWidth); 289 | return false; 290 | } 291 | 292 | std::vector> X; 293 | X.reserve(1 << K); 294 | unsigned char tmpHash[HashOutput]; 295 | for (eh_index i : GetIndicesFromMinimal(soln, CollisionBitLength)) { 296 | GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput); 297 | X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * N/8), 298 | N/8, HashLength, CollisionBitLength, i); 299 | } 300 | 301 | size_t hashLen = HashLength; 302 | size_t lenIndices = sizeof(eh_index); 303 | while (X.size() > 1) { 304 | std::vector> Xc; 305 | for (int i = 0; i < X.size(); i += 2) { 306 | if (!HasCollision(X[i], X[i+1], CollisionByteLength)) { 307 | return false; 308 | } 309 | if (X[i+1].IndicesBefore(X[i], hashLen, lenIndices)) { 310 | return false; 311 | } 312 | if (!DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) { 313 | return false; 314 | } 315 | Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength); 316 | } 317 | X = Xc; 318 | hashLen -= CollisionByteLength; 319 | lenIndices *= 2; 320 | } 321 | 322 | assert(X.size() == 1); 323 | return X[0].IsZero(hashLen); 324 | } 325 | 326 | // Explicit instantiations for Equihash<96,3> 327 | template int Equihash<96,3>::InitialiseState(eh_HashState& base_state); 328 | template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector &soln); 329 | 330 | // Explicit instantiations for Equihash<200,9> 331 | template int Equihash<200,9>::InitialiseState(eh_HashState& base_state); 332 | template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector &soln); 333 | 334 | // Explicit instantiations for Equihash<96,5> 335 | template int Equihash<96,5>::InitialiseState(eh_HashState& base_state); 336 | template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector &soln); 337 | 338 | // Explicit instantiations for Equihash<48,5> 339 | template int Equihash<48,5>::InitialiseState(eh_HashState& base_state); 340 | template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector &soln); 341 | -------------------------------------------------------------------------------- /src/bignum.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto 2 | // Copyright (c) 2009-2012 The Bitcoin developers 3 | // Distributed under the MIT/X11 software license, see the accompanying 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | #ifndef BITCOIN_BIGNUM_H 6 | #define BITCOIN_BIGNUM_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "util.h" // for uint64 13 | 14 | /** Errors thrown by the bignum class */ 15 | class bignum_error : public std::runtime_error 16 | { 17 | public: 18 | explicit bignum_error(const std::string& str) : std::runtime_error(str) {} 19 | }; 20 | 21 | 22 | /** RAII encapsulated BN_CTX (OpenSSL bignum context) */ 23 | class CAutoBN_CTX 24 | { 25 | protected: 26 | BN_CTX* pctx; 27 | BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; } 28 | 29 | public: 30 | CAutoBN_CTX() 31 | { 32 | pctx = BN_CTX_new(); 33 | if (pctx == NULL) 34 | throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); 35 | } 36 | 37 | ~CAutoBN_CTX() 38 | { 39 | if (pctx != NULL) 40 | BN_CTX_free(pctx); 41 | } 42 | 43 | operator BN_CTX*() { return pctx; } 44 | BN_CTX& operator*() { return *pctx; } 45 | BN_CTX** operator&() { return &pctx; } 46 | bool operator!() { return (pctx == NULL); } 47 | }; 48 | 49 | 50 | /** C++ wrapper for BIGNUM (OpenSSL bignum) */ 51 | class CBigNum : public BIGNUM 52 | { 53 | public: 54 | CBigNum() 55 | { 56 | BN_init(this); 57 | } 58 | 59 | CBigNum(const CBigNum& b) 60 | { 61 | BN_init(this); 62 | if (!BN_copy(this, &b)) 63 | { 64 | BN_clear_free(this); 65 | throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); 66 | } 67 | } 68 | 69 | CBigNum& operator=(const CBigNum& b) 70 | { 71 | if (!BN_copy(this, &b)) 72 | throw bignum_error("CBigNum::operator= : BN_copy failed"); 73 | return (*this); 74 | } 75 | 76 | ~CBigNum() 77 | { 78 | BN_clear_free(this); 79 | } 80 | 81 | //CBigNum(char n) is not portable. Use 'signed char' or 'unsigned char'. 82 | CBigNum(signed char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } 83 | CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } 84 | CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } 85 | CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } 86 | CBigNum(int64 n) { BN_init(this); setint64(n); } 87 | CBigNum(unsigned char n) { BN_init(this); setulong(n); } 88 | CBigNum(unsigned short n) { BN_init(this); setulong(n); } 89 | CBigNum(unsigned int n) { BN_init(this); setulong(n); } 90 | CBigNum(unsigned long n) { BN_init(this); setulong(n); } 91 | CBigNum(uint64 n) { BN_init(this); setuint64(n); } 92 | explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } 93 | 94 | explicit CBigNum(const std::vector& vch) 95 | { 96 | BN_init(this); 97 | setvch(vch); 98 | } 99 | 100 | void setulong(unsigned long n) 101 | { 102 | if (!BN_set_word(this, n)) 103 | throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); 104 | } 105 | 106 | unsigned long getulong() const 107 | { 108 | return BN_get_word(this); 109 | } 110 | 111 | unsigned int getuint() const 112 | { 113 | return BN_get_word(this); 114 | } 115 | 116 | int getint() const 117 | { 118 | unsigned long n = BN_get_word(this); 119 | if (!BN_is_negative(this)) 120 | return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::max() : n); 121 | else 122 | return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::min() : -(int)n); 123 | } 124 | 125 | void setint64(int64 sn) 126 | { 127 | unsigned char pch[sizeof(sn) + 6]; 128 | unsigned char* p = pch + 4; 129 | bool fNegative; 130 | uint64 n; 131 | 132 | if (sn < (int64)0) 133 | { 134 | // Since the minimum signed integer cannot be represented as positive so long as its type is signed, 135 | // and it's not well-defined what happens if you make it unsigned before negating it, 136 | // we instead increment the negative integer by 1, convert it, then increment the (now positive) unsigned integer by 1 to compensate 137 | n = -(sn + 1); 138 | ++n; 139 | fNegative = true; 140 | } else { 141 | n = sn; 142 | fNegative = false; 143 | } 144 | 145 | bool fLeadingZeroes = true; 146 | for (int i = 0; i < 8; i++) 147 | { 148 | unsigned char c = (n >> 56) & 0xff; 149 | n <<= 8; 150 | if (fLeadingZeroes) 151 | { 152 | if (c == 0) 153 | continue; 154 | if (c & 0x80) 155 | *p++ = (fNegative ? 0x80 : 0); 156 | else if (fNegative) 157 | c |= 0x80; 158 | fLeadingZeroes = false; 159 | } 160 | *p++ = c; 161 | } 162 | unsigned int nSize = p - (pch + 4); 163 | pch[0] = (nSize >> 24) & 0xff; 164 | pch[1] = (nSize >> 16) & 0xff; 165 | pch[2] = (nSize >> 8) & 0xff; 166 | pch[3] = (nSize) & 0xff; 167 | BN_mpi2bn(pch, p - pch, this); 168 | } 169 | 170 | void setuint64(uint64 n) 171 | { 172 | unsigned char pch[sizeof(n) + 6]; 173 | unsigned char* p = pch + 4; 174 | bool fLeadingZeroes = true; 175 | for (int i = 0; i < 8; i++) 176 | { 177 | unsigned char c = (n >> 56) & 0xff; 178 | n <<= 8; 179 | if (fLeadingZeroes) 180 | { 181 | if (c == 0) 182 | continue; 183 | if (c & 0x80) 184 | *p++ = 0; 185 | fLeadingZeroes = false; 186 | } 187 | *p++ = c; 188 | } 189 | unsigned int nSize = p - (pch + 4); 190 | pch[0] = (nSize >> 24) & 0xff; 191 | pch[1] = (nSize >> 16) & 0xff; 192 | pch[2] = (nSize >> 8) & 0xff; 193 | pch[3] = (nSize) & 0xff; 194 | BN_mpi2bn(pch, p - pch, this); 195 | } 196 | 197 | void setuint256(uint256 n) 198 | { 199 | unsigned char pch[sizeof(n) + 6]; 200 | unsigned char* p = pch + 4; 201 | bool fLeadingZeroes = true; 202 | unsigned char* pbegin = (unsigned char*)&n; 203 | unsigned char* psrc = pbegin + sizeof(n); 204 | while (psrc != pbegin) 205 | { 206 | unsigned char c = *(--psrc); 207 | if (fLeadingZeroes) 208 | { 209 | if (c == 0) 210 | continue; 211 | if (c & 0x80) 212 | *p++ = 0; 213 | fLeadingZeroes = false; 214 | } 215 | *p++ = c; 216 | } 217 | unsigned int nSize = p - (pch + 4); 218 | pch[0] = (nSize >> 24) & 0xff; 219 | pch[1] = (nSize >> 16) & 0xff; 220 | pch[2] = (nSize >> 8) & 0xff; 221 | pch[3] = (nSize >> 0) & 0xff; 222 | BN_mpi2bn(pch, p - pch, this); 223 | } 224 | 225 | uint256 getuint256() const 226 | { 227 | unsigned int nSize = BN_bn2mpi(this, NULL); 228 | if (nSize < 4) 229 | return 0; 230 | std::vector vch(nSize); 231 | BN_bn2mpi(this, &vch[0]); 232 | if (vch.size() > 4) 233 | vch[4] &= 0x7f; 234 | uint256 n = 0; 235 | for (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) 236 | ((unsigned char*)&n)[i] = vch[j]; 237 | return n; 238 | } 239 | 240 | void setvch(const std::vector& vch) 241 | { 242 | std::vector vch2(vch.size() + 4); 243 | unsigned int nSize = vch.size(); 244 | // BIGNUM's byte stream format expects 4 bytes of 245 | // big endian size data info at the front 246 | vch2[0] = (nSize >> 24) & 0xff; 247 | vch2[1] = (nSize >> 16) & 0xff; 248 | vch2[2] = (nSize >> 8) & 0xff; 249 | vch2[3] = (nSize >> 0) & 0xff; 250 | // swap data to big endian 251 | reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); 252 | BN_mpi2bn(&vch2[0], vch2.size(), this); 253 | } 254 | 255 | std::vector getvch() const 256 | { 257 | unsigned int nSize = BN_bn2mpi(this, NULL); 258 | if (nSize <= 4) 259 | return std::vector(); 260 | std::vector vch(nSize); 261 | BN_bn2mpi(this, &vch[0]); 262 | vch.erase(vch.begin(), vch.begin() + 4); 263 | reverse(vch.begin(), vch.end()); 264 | return vch; 265 | } 266 | 267 | // The "compact" format is a representation of a whole 268 | // number N using an unsigned 32bit number similar to a 269 | // floating point format. 270 | // The most significant 8 bits are the unsigned exponent of base 256. 271 | // This exponent can be thought of as "number of bytes of N". 272 | // The lower 23 bits are the mantissa. 273 | // Bit number 24 (0x800000) represents the sign of N. 274 | // N = (-1^sign) * mantissa * 256^(exponent-3) 275 | // 276 | // Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). 277 | // MPI uses the most significant bit of the first byte as sign. 278 | // Thus 0x1234560000 is compact (0x05123456) 279 | // and 0xc0de000000 is compact (0x0600c0de) 280 | // (0x05c0de00) would be -0x40de000000 281 | // 282 | // Bitcoin only uses this "compact" format for encoding difficulty 283 | // targets, which are unsigned 256bit quantities. Thus, all the 284 | // complexities of the sign bit and using base 256 are probably an 285 | // implementation accident. 286 | // 287 | // This implementation directly uses shifts instead of going 288 | // through an intermediate MPI representation. 289 | CBigNum& SetCompact(unsigned int nCompact) 290 | { 291 | unsigned int nSize = nCompact >> 24; 292 | bool fNegative =(nCompact & 0x00800000) != 0; 293 | unsigned int nWord = nCompact & 0x007fffff; 294 | if (nSize <= 3) 295 | { 296 | nWord >>= 8*(3-nSize); 297 | BN_set_word(this, nWord); 298 | } 299 | else 300 | { 301 | BN_set_word(this, nWord); 302 | BN_lshift(this, this, 8*(nSize-3)); 303 | } 304 | BN_set_negative(this, fNegative); 305 | return *this; 306 | } 307 | 308 | unsigned int GetCompact() const 309 | { 310 | unsigned int nSize = BN_num_bytes(this); 311 | unsigned int nCompact = 0; 312 | if (nSize <= 3) 313 | nCompact = BN_get_word(this) << 8*(3-nSize); 314 | else 315 | { 316 | CBigNum bn; 317 | BN_rshift(&bn, this, 8*(nSize-3)); 318 | nCompact = BN_get_word(&bn); 319 | } 320 | // The 0x00800000 bit denotes the sign. 321 | // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. 322 | if (nCompact & 0x00800000) 323 | { 324 | nCompact >>= 8; 325 | nSize++; 326 | } 327 | nCompact |= nSize << 24; 328 | nCompact |= (BN_is_negative(this) ? 0x00800000 : 0); 329 | return nCompact; 330 | } 331 | 332 | void SetHex(const std::string& str) 333 | { 334 | // skip 0x 335 | const char* psz = str.c_str(); 336 | while (isspace(*psz)) 337 | psz++; 338 | bool fNegative = false; 339 | if (*psz == '-') 340 | { 341 | fNegative = true; 342 | psz++; 343 | } 344 | if (psz[0] == '0' && tolower(psz[1]) == 'x') 345 | psz += 2; 346 | while (isspace(*psz)) 347 | psz++; 348 | 349 | // hex string to bignum 350 | static const signed char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; 351 | *this = 0; 352 | while (isxdigit(*psz)) 353 | { 354 | *this <<= 4; 355 | int n = phexdigit[(unsigned char)*psz++]; 356 | *this += n; 357 | } 358 | if (fNegative) 359 | *this = 0 - *this; 360 | } 361 | 362 | std::string ToString(int nBase=10) const 363 | { 364 | CAutoBN_CTX pctx; 365 | CBigNum bnBase = nBase; 366 | CBigNum bn0 = 0; 367 | std::string str; 368 | CBigNum bn = *this; 369 | BN_set_negative(&bn, false); 370 | CBigNum dv; 371 | CBigNum rem; 372 | if (BN_cmp(&bn, &bn0) == 0) 373 | return "0"; 374 | while (BN_cmp(&bn, &bn0) > 0) 375 | { 376 | if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) 377 | throw bignum_error("CBigNum::ToString() : BN_div failed"); 378 | bn = dv; 379 | unsigned int c = rem.getulong(); 380 | str += "0123456789abcdef"[c]; 381 | } 382 | if (BN_is_negative(this)) 383 | str += "-"; 384 | reverse(str.begin(), str.end()); 385 | return str; 386 | } 387 | 388 | std::string GetHex() const 389 | { 390 | return ToString(16); 391 | } 392 | 393 | /*unsigned int GetSerializeSize(int nType=0, int nVersion=PROTOCOL_VERSION) const 394 | { 395 | return ::GetSerializeSize(getvch(), nType, nVersion); 396 | } 397 | 398 | template 399 | void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const 400 | { 401 | ::Serialize(s, getvch(), nType, nVersion); 402 | } 403 | 404 | template 405 | void Unserialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) 406 | { 407 | std::vector vch; 408 | ::Unserialize(s, vch, nType, nVersion); 409 | setvch(vch); 410 | }*/ 411 | 412 | 413 | bool operator!() const 414 | { 415 | return BN_is_zero(this); 416 | } 417 | 418 | CBigNum& operator+=(const CBigNum& b) 419 | { 420 | if (!BN_add(this, this, &b)) 421 | throw bignum_error("CBigNum::operator+= : BN_add failed"); 422 | return *this; 423 | } 424 | 425 | CBigNum& operator-=(const CBigNum& b) 426 | { 427 | *this = *this - b; 428 | return *this; 429 | } 430 | 431 | CBigNum& operator*=(const CBigNum& b) 432 | { 433 | CAutoBN_CTX pctx; 434 | if (!BN_mul(this, this, &b, pctx)) 435 | throw bignum_error("CBigNum::operator*= : BN_mul failed"); 436 | return *this; 437 | } 438 | 439 | CBigNum& operator/=(const CBigNum& b) 440 | { 441 | *this = *this / b; 442 | return *this; 443 | } 444 | 445 | CBigNum& operator%=(const CBigNum& b) 446 | { 447 | *this = *this % b; 448 | return *this; 449 | } 450 | 451 | CBigNum& operator<<=(unsigned int shift) 452 | { 453 | if (!BN_lshift(this, this, shift)) 454 | throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); 455 | return *this; 456 | } 457 | 458 | CBigNum& operator>>=(unsigned int shift) 459 | { 460 | // Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number 461 | // if built on ubuntu 9.04 or 9.10, probably depends on version of OpenSSL 462 | CBigNum a = 1; 463 | a <<= shift; 464 | if (BN_cmp(&a, this) > 0) 465 | { 466 | *this = 0; 467 | return *this; 468 | } 469 | 470 | if (!BN_rshift(this, this, shift)) 471 | throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); 472 | return *this; 473 | } 474 | 475 | 476 | CBigNum& operator++() 477 | { 478 | // prefix operator 479 | if (!BN_add(this, this, BN_value_one())) 480 | throw bignum_error("CBigNum::operator++ : BN_add failed"); 481 | return *this; 482 | } 483 | 484 | const CBigNum operator++(int) 485 | { 486 | // postfix operator 487 | const CBigNum ret = *this; 488 | ++(*this); 489 | return ret; 490 | } 491 | 492 | CBigNum& operator--() 493 | { 494 | // prefix operator 495 | CBigNum r; 496 | if (!BN_sub(&r, this, BN_value_one())) 497 | throw bignum_error("CBigNum::operator-- : BN_sub failed"); 498 | *this = r; 499 | return *this; 500 | } 501 | 502 | const CBigNum operator--(int) 503 | { 504 | // postfix operator 505 | const CBigNum ret = *this; 506 | --(*this); 507 | return ret; 508 | } 509 | 510 | 511 | friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); 512 | friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); 513 | friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); 514 | }; 515 | 516 | 517 | 518 | inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) 519 | { 520 | CBigNum r; 521 | if (!BN_add(&r, &a, &b)) 522 | throw bignum_error("CBigNum::operator+ : BN_add failed"); 523 | return r; 524 | } 525 | 526 | inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) 527 | { 528 | CBigNum r; 529 | if (!BN_sub(&r, &a, &b)) 530 | throw bignum_error("CBigNum::operator- : BN_sub failed"); 531 | return r; 532 | } 533 | 534 | inline const CBigNum operator-(const CBigNum& a) 535 | { 536 | CBigNum r(a); 537 | BN_set_negative(&r, !BN_is_negative(&r)); 538 | return r; 539 | } 540 | 541 | inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) 542 | { 543 | CAutoBN_CTX pctx; 544 | CBigNum r; 545 | if (!BN_mul(&r, &a, &b, pctx)) 546 | throw bignum_error("CBigNum::operator* : BN_mul failed"); 547 | return r; 548 | } 549 | 550 | inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) 551 | { 552 | CAutoBN_CTX pctx; 553 | CBigNum r; 554 | if (!BN_div(&r, NULL, &a, &b, pctx)) 555 | throw bignum_error("CBigNum::operator/ : BN_div failed"); 556 | return r; 557 | } 558 | 559 | inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) 560 | { 561 | CAutoBN_CTX pctx; 562 | CBigNum r; 563 | if (!BN_mod(&r, &a, &b, pctx)) 564 | throw bignum_error("CBigNum::operator% : BN_div failed"); 565 | return r; 566 | } 567 | 568 | inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) 569 | { 570 | CBigNum r; 571 | if (!BN_lshift(&r, &a, shift)) 572 | throw bignum_error("CBigNum:operator<< : BN_lshift failed"); 573 | return r; 574 | } 575 | 576 | inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) 577 | { 578 | CBigNum r = a; 579 | r >>= shift; 580 | return r; 581 | } 582 | 583 | inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); } 584 | inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); } 585 | inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); } 586 | inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); } 587 | inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); } 588 | inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); } 589 | 590 | #endif 591 | -------------------------------------------------------------------------------- /src/zcashpool.cpp: -------------------------------------------------------------------------------- 1 | #include "zcashpool.h" 2 | #include "address.h" 3 | #include "poolcommon/poolapi.h" 4 | #include "poolcore/backend.h" 5 | #include "loguru.hpp" 6 | #include "p2p/p2p.h" 7 | #include "equihash_original.h" 8 | #include 9 | 10 | #include "block.h" 11 | #include 12 | 13 | unsigned writeCompactSize(size_t size, uint8_t *out) 14 | { 15 | if (size < 253) { 16 | out[0] = size; 17 | return 1; 18 | } else if (size <= std::numeric_limits::max()) { 19 | out[0] = 253; 20 | *(uint16_t*)(out+1) = size; 21 | return 3; 22 | } else if (size <= std::numeric_limits::max()) { 23 | out[0] = 254; 24 | *(uint32_t*)(out+1) = size; 25 | return 5; 26 | } else { 27 | out[0] = 255; 28 | *(uint64_t*)(out+1) = size; 29 | } 30 | return 0; 31 | } 32 | 33 | bool equiHashShareCheck(const CBlockHeader *header, const uint8_t *proof, size_t size) 34 | { 35 | crypto_generichash_blake2b_state state; 36 | Eh200_9.InitialiseState(state); 37 | crypto_generichash_blake2b_update(&state, (const uint8_t*)&header->data, sizeof(header->data)); 38 | crypto_generichash_blake2b_update(&state, (const uint8_t*)header->nNonce.begin(), 32); 39 | std::vector proofForCheck(proof, proof+size); 40 | return Eh200_9.IsValidSolution(state, proofForCheck); 41 | } 42 | 43 | bool checkRequest(poolContext *context, 44 | pool::proto::Request &req, 45 | pool::proto::Reply &rep, 46 | void *msg, 47 | size_t msgSize) 48 | { 49 | if (!req.ParseFromArray(msg, msgSize)) { 50 | LOG_F(WARNING, "invalid message received"); 51 | return false; 52 | } 53 | 54 | rep.Clear(); 55 | rep.set_type(req.type()); 56 | rep.set_reqid(req.reqid()); 57 | rep.set_error(pool::proto::Reply::NONE); 58 | 59 | if(req.has_height() && req.height() < context->mCurrBlock.height()) { 60 | rep.mutable_block()->CopyFrom(context->mCurrBlock); 61 | } 62 | return true; 63 | } 64 | 65 | 66 | void onConnect(poolContext *context, pool::proto::Request &req, pool::proto::Reply &rep) 67 | { 68 | // TODO: check version 69 | bool versionIsValid = true; 70 | 71 | if (!versionIsValid) { 72 | rep.set_error(pool::proto::Reply::VERSION); 73 | rep.set_errstr("Your miner version will no longer be supported in the near future. Please upgrade."); 74 | } 75 | 76 | // fill 'bitcoin' and 'signals' host addresses 77 | pool::proto::ServerInfo mServerInfo; 78 | mServerInfo.set_host(context->xpmclientHost); 79 | mServerInfo.set_router(context->xpmclientWorkPort); 80 | mServerInfo.set_pub(context->xpmclientWorkPort+1); 81 | mServerInfo.set_target(context->difficulty != 0 ? (unsigned)context->difficulty : 10); 82 | 83 | rep.mutable_sinfo()->CopyFrom(mServerInfo); 84 | } 85 | 86 | 87 | void onGetWork(poolContext *context, pool::proto::Request &req, pool::proto::Reply &rep, bool *needDisconnect) 88 | { 89 | // getblocktemplate call 90 | *needDisconnect = false; 91 | auto block = ioGetBlockTemplate(context->client); 92 | if (!block) { 93 | *needDisconnect = true; 94 | rep.set_error(pool::proto::Reply::HEIGHT); 95 | rep.set_errstr("Can't receive work from ZCash wallet"); 96 | return; 97 | } 98 | 99 | context->extraNonceMap[block->merkle.c_str()] = block->extraNonce; 100 | 101 | pool::proto::Work* work = rep.mutable_work(); 102 | work->set_height(context->mCurrBlock.height()); 103 | work->set_merkle(block->merkle.c_str()); 104 | work->set_time(std::max(static_cast(time(0)), block->time)); 105 | work->set_bits(block->bits); 106 | work->set_hashreserved(block->hashreserved.c_str()); 107 | work->set_k(block->equilHashK); 108 | work->set_n(block->equilHashN); 109 | } 110 | 111 | void onShare(poolContext *context, pool::proto::Request &req, pool::proto::Reply &rep, bool *needDisconnect) 112 | { 113 | *needDisconnect = false; 114 | if (!context->mCurrBlock.has_height()) { 115 | rep.set_error(pool::proto::Reply::STALE); 116 | return; 117 | } 118 | 119 | // share existing 120 | if (!req.has_share() || !req.share().has_bignonce() || !req.share().has_proofofwork()) { 121 | LOG_F(WARNING, "!req.has_share()"); 122 | rep.set_error(pool::proto::Reply::INVALID); 123 | return; 124 | } 125 | 126 | // block height 127 | const pool::proto::Share& share = req.share(); 128 | if (share.height() != context->mCurrBlock.height()) { 129 | rep.set_error(pool::proto::Reply::STALE); 130 | return; 131 | } 132 | 133 | // merkle must be present at extraNonce map 134 | auto nonceIt = context->extraNonceMap.find(share.merkle()); 135 | if (nonceIt == context->extraNonceMap.end()) { 136 | *needDisconnect = true; 137 | LOG_F(WARNING, "share for unknown work"); 138 | rep.set_error(pool::proto::Reply::STALE); 139 | return; 140 | } 141 | 142 | // check share 143 | unsigned value = 0; 144 | mpz_class mpzValue; 145 | mpz_class mpzHash; 146 | 147 | uint256 shareHeaderHash; 148 | 149 | { 150 | CBlockHeader header; 151 | header.data.nVersion = CBlockHeader::CURRENT_VERSION; 152 | header.data.hashPrevBlock.SetHex(context->mCurrBlock.hash()); 153 | header.data.hashMerkleRoot.SetHex(share.merkle()); 154 | // TODO: from work 155 | header.data.hashReserved = 0; 156 | header.data.nTime = share.time(); 157 | header.data.nBits = share.bits(); 158 | header.nNonce.SetHex(share.bignonce()); 159 | 160 | uint8_t compactSizeData[16]; 161 | unsigned compactSize = writeCompactSize(share.proofofwork().size(), compactSizeData); 162 | 163 | SHA256_CTX ctx; 164 | uint8_t hash1[32]; 165 | SHA256_Init(&ctx); 166 | SHA256_Update(&ctx, &header.data, sizeof(header.data)); 167 | SHA256_Update(&ctx, header.nNonce.begin(), 32); 168 | SHA256_Update(&ctx, compactSizeData, compactSize); 169 | SHA256_Update(&ctx, share.proofofwork().data(), share.proofofwork().size()); 170 | SHA256_Final(hash1, &ctx); 171 | SHA256_Init(&ctx); 172 | SHA256_Update(&ctx, hash1, sizeof(hash1)); 173 | SHA256_Final(shareHeaderHash.begin(), &ctx); 174 | 175 | uint256 receivedHash; 176 | receivedHash.SetHex(share.hash()); 177 | if (receivedHash != shareHeaderHash) { 178 | LOG_F(WARNING, "invalid share(sha256), no penalty"); 179 | rep.set_error(pool::proto::Reply::INVALID); 180 | return; 181 | } 182 | 183 | // warning: need a huge CPU power for share check 184 | if (context->equihashShareCheck) { 185 | if (!equiHashShareCheck(&header, (const uint8_t*)share.proofofwork().data(), share.proofofwork().size())) { 186 | LOG_F(WARNING, "invalid share(equi), do a penalty"); 187 | rep.set_error(pool::proto::Reply::INVALID); 188 | } 189 | } 190 | } 191 | 192 | if (shareHeaderHash > context->shareTarget) { 193 | LOG_F(WARNING, "too small share"); 194 | rep.set_error(pool::proto::Reply::INVALID); 195 | return; 196 | } 197 | 198 | // for static diff all shares have same weight 199 | value = 1; 200 | 201 | // check duplicate 202 | if (!context->uniqueShares.insert(shareHeaderHash).second) { 203 | LOG_F(WARNING, "duplicate share"); 204 | rep.set_error(pool::proto::Reply::DUPLICATE); 205 | return; 206 | } 207 | 208 | if (value) { 209 | int64_t generatedCoins = 0; 210 | if (share.isblock()) { 211 | LOG_F(INFO, "found block %s", shareHeaderHash.ToString().c_str()); 212 | auto result = ioSendProofOfWork(context->client, 213 | context->mCurrBlock.height(), 214 | share.time(), 215 | share.bignonce(), 216 | nonceIt->second, 217 | share.proofofwork()); 218 | if (!result) { 219 | rep.set_error(pool::proto::Reply::INVALID); 220 | return; 221 | } 222 | 223 | if (result->result == true) { 224 | generatedCoins = result->generatedCoins; 225 | } else { 226 | LOG_F(ERROR, "proofOfWork check failed"); 227 | rep.set_error(pool::proto::Reply::INVALID); 228 | return; 229 | } 230 | } 231 | 232 | // send share information to backend 233 | { 234 | flatbuffers::FlatBufferBuilder fbb; 235 | auto shareOffset = CreateShare(fbb, 236 | context->mCurrBlock.height()+1, 237 | fbb.CreateString(share.addr()), 238 | value, 239 | share.isblock(), 240 | fbb.CreateString(shareHeaderHash.ToString()), 241 | generatedCoins); 242 | fbb.Finish(CreateP2PMessage(fbb, FunctionId_Share, Data_Share, shareOffset.Union())); 243 | if (!context->backend->sendMessage(context->base, fbb.GetBufferPointer(), fbb.GetSize())) { 244 | LOG_F(ERROR, "can't send share to backend"); 245 | rep.set_error(pool::proto::Reply::STALE); 246 | return; 247 | } 248 | } 249 | 250 | } else { 251 | LOG_F(WARNING, "invalid share"); 252 | rep.set_error(pool::proto::Reply::INVALID); 253 | return; 254 | } 255 | } 256 | 257 | void onStats(poolContext *context, pool::proto::Request &req, pool::proto::Reply &rep) 258 | { 259 | if (!req.has_stats()) { 260 | LOG_F(WARNING, "!req.has_stats()"); 261 | return; 262 | } 263 | 264 | const pool::proto::ClientStats &stats = req.stats(); 265 | flatbuffers::FlatBufferBuilder fbb; 266 | auto statsOffset = CreateStats(fbb, 267 | fbb.CreateString(stats.addr()), 268 | fbb.CreateString(stats.name()), 269 | (int64_t)(stats.cpd()*1000.0), 270 | stats.latency(), 271 | fbb.CreateString(""), 272 | stats.has_unittype() ? (UnitType)stats.unittype() : UnitType_GPU, 273 | stats.ngpus(), 274 | stats.temp()); 275 | fbb.Finish(CreateP2PMessage(fbb, FunctionId_Stats, Data_Stats, statsOffset.Union())); 276 | if (!context->backend->sendMessage(context->base, fbb.GetBufferPointer(), fbb.GetSize())) { 277 | LOG_F(ERROR, "can't send stats to backend"); 278 | return; 279 | } 280 | } 281 | 282 | static bool splitStratumWorkerName(char *input, const char **wallet, const char **worker) 283 | { 284 | char *s = strchr(input, '.'); 285 | if (s) { 286 | *s = 0; 287 | *wallet = input; 288 | *worker = s+1; 289 | return true; 290 | } else { 291 | *wallet = input; 292 | *worker = "default"; 293 | return true; 294 | } 295 | } 296 | 297 | 298 | int64_t stratumCheckShare(poolContext *context, 299 | aioObject *socket, 300 | StratumMessage *msg, 301 | uint256 *shareHeaderHash, 302 | const char **error, 303 | int64_t *generatedCoins) 304 | { 305 | if (!context->mCurrBlock.has_height()) { 306 | *error = "\"Stale share\""; 307 | return -1; 308 | } 309 | 310 | const char *extraNoncePos = strchr(msg->submit.jobId.c_str(), '#'); 311 | if (!extraNoncePos) { 312 | *error = "\"Invalid job name\""; 313 | return -1; 314 | } 315 | 316 | int64_t extraNonce = xatoi(extraNoncePos+1); 317 | const StratumTask &task = context->stratumTaskMap[extraNonce]; 318 | if (task.merkle.empty()) { 319 | *error = "\"Share for unknown work\""; 320 | return -1; 321 | } 322 | 323 | if (msg->submit.equihashSolution.size() < 4) { 324 | *error = "\"Invalid share(too short solution)\""; 325 | return -1; 326 | } 327 | 328 | unsigned value = 0; 329 | mpz_class mpzValue; 330 | mpz_class mpzHash; 331 | 332 | CBlockHeader header; 333 | header.data.nVersion = CBlockHeader::CURRENT_VERSION; 334 | header.data.hashPrevBlock.SetHex(context->mCurrBlock.hash()); 335 | header.data.hashMerkleRoot.SetHex(task.merkle); 336 | header.data.hashReserved.SetHex(task.hashReserved); 337 | header.data.nTime = msg->submit.time; 338 | header.data.nBits = task.bits; 339 | 340 | // hi 32 bits always 0 341 | memset(header.nNonce.begin(), 0, 4); 342 | memcpy(header.nNonce.begin()+4, msg->submit.nonce, 28); 343 | 344 | const uint8_t *equihashSolution = msg->submit.equihashSolution.data()+3; 345 | size_t equihashSolutionSize = msg->submit.equihashSolution.size()-3; 346 | uint8_t compactSizeData[16]; 347 | unsigned compactSize = writeCompactSize(equihashSolutionSize, compactSizeData); 348 | 349 | SHA256_CTX ctx; 350 | uint8_t hash1[32]; 351 | SHA256_Init(&ctx); 352 | SHA256_Update(&ctx, &header.data, sizeof(header.data)); 353 | SHA256_Update(&ctx, header.nNonce.begin(), 32); 354 | SHA256_Update(&ctx, compactSizeData, compactSize); 355 | SHA256_Update(&ctx, equihashSolution, equihashSolutionSize); 356 | SHA256_Final(hash1, &ctx); 357 | SHA256_Init(&ctx); 358 | SHA256_Update(&ctx, hash1, sizeof(hash1)); 359 | SHA256_Final(shareHeaderHash->begin(), &ctx); 360 | 361 | if (context->equihashShareCheck) { 362 | if (!equiHashShareCheck(&header, equihashSolution, equihashSolutionSize)) { 363 | *error = "\"Invalid share(equi)\""; 364 | return -1; 365 | } 366 | } 367 | 368 | if (*shareHeaderHash > context->shareTarget) { 369 | *error = "\"Too small share\""; 370 | return -1; 371 | } 372 | 373 | // for static diff all shares have same weight 374 | value = 1; 375 | 376 | // check duplicate 377 | if (!context->uniqueShares.insert(*shareHeaderHash).second) { 378 | *error = "\"Duplicate share\""; 379 | return -1; 380 | } 381 | 382 | if (*shareHeaderHash <= context->blockTarget) { 383 | LOG_F(INFO, "(stratum) found block %s", shareHeaderHash->ToString().c_str()); 384 | std::string proofofwork((const char*)equihashSolution, equihashSolutionSize); 385 | auto result = ioSendProofOfWork(context->client, 386 | context->mCurrBlock.height(), 387 | header.data.nTime, 388 | header.nNonce.GetHex(), 389 | extraNonce, 390 | proofofwork); 391 | if (!result || result->result == false) { 392 | *error = "\"Check proof of work failed\""; 393 | return -1; 394 | } 395 | 396 | *generatedCoins = result->generatedCoins; 397 | } 398 | 399 | return value; 400 | } 401 | 402 | 403 | void onStratumSubscribe(poolContext *context, aioObject *socket, StratumMessage *msg, int64_t sessionId) 404 | { 405 | // {"id": 1, "result": ["SESSION_ID", "NONCE_1"], "error": null} \n 406 | char sessionBuffer[32]; 407 | char response[256]; 408 | xitoa(sessionId, sessionBuffer); 409 | snprintf(response, sizeof(response), 410 | "{\"id\": %i, \"result\": [\"%s\", \"00000000\"], \"error\": null}\n", 411 | (int)msg->id, 412 | sessionBuffer); 413 | aioWrite(socket, response, strlen(response), afWaitAll, 0, 0, 0); 414 | } 415 | 416 | void onStratumAuthorize(poolContext *context, aioObject *socket, StratumMessage *msg, int64_t sessionId) 417 | { 418 | // {"id": 2, "result": AUTHORIZED, "error": ERROR} \n 419 | char response[256]; 420 | const char *authorized; 421 | const char *error = "null"; 422 | 423 | const char *wallet; 424 | const char *workerName; 425 | if (splitStratumWorkerName((char*)msg->authorize.login.data(), &wallet, &workerName)) { 426 | CZECAddress address(wallet); 427 | if (!context->checkAddress || address.isValid()) { 428 | authorized = "true"; 429 | } else { 430 | authorized = "false"; 431 | error = "\"Invalid ZCash T-Address sent at a login\""; 432 | } 433 | } else { 434 | authorized = "false"; 435 | error = "\"Invalid login format, should be Address/WorkerName\""; 436 | } 437 | 438 | snprintf(response, sizeof(response), 439 | "{\"id\": %i, \"result\": %s, \"error\": %s}\n", 440 | (int)msg->id, 441 | authorized, 442 | error); 443 | aioWrite(socket, response, strlen(response), afWaitAll, 0, 0, 0); 444 | 445 | // add worker record 446 | StratumWorker worker; 447 | worker.wallet = wallet; 448 | worker.worker = workerName; 449 | worker.socket = socket; 450 | context->stratumWorkers[sessionId] = worker; 451 | } 452 | 453 | void onStratumSubmit(poolContext *context, aioObject *socket, StratumMessage *msg, int64_t sessionId) 454 | { 455 | // {"id": 4, "method": "mining.submit", "params": ["WORKER_NAME", "JOB_ID", "TIME", "NONCE_2", "EQUIHASH_SOLUTION"]} \n 456 | int64_t value = -1; 457 | const char *error = "null"; 458 | const char *wallet; 459 | const char *workerName; 460 | if (splitStratumWorkerName((char*)msg->submit.workerName.data(), &wallet, &workerName)) { 461 | int64_t generatedCoins = 0; 462 | uint256 shareHeaderHash = 0; 463 | value = stratumCheckShare(context, socket, msg, &shareHeaderHash, &error, &generatedCoins); 464 | if (value > 0) { 465 | flatbuffers::FlatBufferBuilder fbb; 466 | auto shareOffset = CreateShare(fbb, 467 | context->mCurrBlock.height()+1, 468 | fbb.CreateString(wallet), 469 | value, 470 | generatedCoins > 0, 471 | fbb.CreateString(shareHeaderHash.ToString()), 472 | generatedCoins); 473 | 474 | fbb.Finish(CreateP2PMessage(fbb, FunctionId_Share, Data_Share, shareOffset.Union())); 475 | if (!context->backend->sendMessage(context->base, fbb.GetBufferPointer(), fbb.GetSize())) { 476 | value = -1; 477 | error = "\"Stale share\""; 478 | } 479 | } else { 480 | LOG_F(WARNING, "share check error: %s", error); 481 | } 482 | } else { 483 | value = -1; 484 | error = "\"Invalid login format\""; 485 | } 486 | 487 | // {"id": 4, "result": ACCEPTED, "error": ERROR}\n 488 | char response[256]; 489 | snprintf(response, sizeof(response), 490 | "{\"id\": %i, \"result\": %s, \"error\": %s}\n", 491 | (int)msg->id, 492 | (value>0 ? "true" : "false"), 493 | error); 494 | 495 | aioWrite(socket, response, strlen(response), afWaitAll, 0, 0, 0); 496 | if (value > 0) { 497 | context->stratumWorkers[sessionId].pushShare(); 498 | } else { 499 | stratumSendNewWork(context, socket, sessionId); 500 | } 501 | } 502 | 503 | void stratumSendSetTarget(poolContext *context, aioObject *socket) 504 | { 505 | char message[256]; 506 | // TODO: take minimal share from context, don't send 507 | // 0080000000000000000000000000000000000000000000000000000000000000 508 | snprintf(message, sizeof(message), 509 | "{\"id\": null, \"method\": \"mining.set_target\", \"params\": [\"%s\"]}\n", 510 | context->shareTargetForStratum.c_str()); 511 | 512 | aioWrite(socket, message, strlen(message), afWaitAll, 0, 0, 0); 513 | } 514 | 515 | bool stratumSendNewWork(poolContext *context, aioObject *socket, int64_t sessionId) 516 | { 517 | //{"id": null, "method": "mining.notify", "params": ["JOB_ID", "VERSION", "PREVHASH", "MERKLEROOT", "RESERVED", "TIME", "BITS", CLEAN_JOBS]} \n 518 | 519 | // getblocktemplate call 520 | auto block = ioGetBlockTemplate(context->client); 521 | if (!block || context->mCurrBlock.hash().empty() || block->merkle.empty()) 522 | return false; 523 | 524 | char message[4096]; 525 | char extraNonceBuffer[64]; 526 | char prevHash[128]; 527 | char merkleroot[128]; 528 | char reserved[128]; 529 | char timeInHex[16]; 530 | char bitsInHex[16]; 531 | sprintf(extraNonceBuffer, "%u#%li", (unsigned)context->mCurrBlock.height()+1, (long)block->extraNonce); 532 | stratumLittleEndianHex(context->mCurrBlock.hash().c_str(), context->mCurrBlock.hash().size(), prevHash); 533 | stratumLittleEndianHex(block->merkle.c_str(), block->merkle.size(), merkleroot); 534 | stratumLittleEndianHex(block->hashreserved.c_str(), block->hashreserved.size(), reserved); 535 | stratumDumpHex(static_cast(std::max(static_cast(time(0)), block->time)), timeInHex); 536 | stratumDumpHex((uint32_t)block->bits, bitsInHex); 537 | 538 | snprintf(message, sizeof(message), 539 | "{\"id\": null, \"method\": \"mining.notify\", \"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", true]}\n", 540 | extraNonceBuffer, // JOB_ID = extraNonce 541 | "04000000", // VERSION 542 | prevHash, // PREVHASH 543 | merkleroot, // MERKLEROOT 544 | reserved, // RESERVED 545 | timeInHex, // TIME 546 | bitsInHex); // BITS 547 | 548 | aioWrite(socket, message, strlen(message), afWaitAll, 0, 0, 0); 549 | 550 | StratumTask task; 551 | task.merkle = block->merkle; 552 | task.bits = block->bits; 553 | task.hashReserved = block->hashreserved; 554 | context->stratumTaskMap[block->extraNonce] = task; 555 | context->stratumWorkers[sessionId].lastUpdateTime = time(0); 556 | return true; 557 | } 558 | 559 | 560 | void stratumSendStats(poolContext *context, StratumWorker &worker) 561 | { 562 | double solsPerSec = worker.updateStats() * context->shareTargetCoeff; 563 | 564 | flatbuffers::FlatBufferBuilder fbb; 565 | auto statsOffset = CreateStats(fbb, 566 | fbb.CreateString(worker.wallet), 567 | fbb.CreateString(worker.worker), 568 | (unsigned)(solsPerSec*1000.0), 569 | -1, 570 | fbb.CreateString(""), 571 | UnitType_GPU, 572 | 0, 573 | 0); 574 | fbb.Finish(CreateP2PMessage(fbb, FunctionId_Stats, Data_Stats, statsOffset.Union())); 575 | if (!context->backend->sendMessage(context->base, fbb.GetBufferPointer(), fbb.GetSize())) { 576 | LOG_F(ERROR, "can't send stats to backend"); 577 | return; 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "zcashpool.h" 2 | #include "address.h" 3 | #include "stratum.h" 4 | #include "poolcore/backend.h" 5 | #include "poolcommon/poolapi.h" 6 | #include "loguru.hpp" 7 | 8 | #include "asyncio/coroutine.h" 9 | #include "asyncio/socket.h" 10 | #include "asyncioextras/zmtp.h" 11 | #include "p2p/p2p.h" 12 | #include "p2putils/coreTypes.h" 13 | #include "p2putils/uriParse.h" 14 | 15 | __NO_DEPRECATED_BEGIN 16 | #include "config4cpp/Configuration.h" 17 | __NO_DEPRECATED_END 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "uint256.h" 24 | 25 | #define TM 5000000 26 | 27 | static poolContext *gPoolContext; 28 | 29 | struct listenerContext { 30 | asyncBase *base; 31 | aioObject *socket; 32 | coroutineProcTy *proc; 33 | void *arg; 34 | }; 35 | 36 | inline void mpz_from_uint256(mpz_t r, uint256& u) 37 | { 38 | mpz_import(r, 32 / sizeof(unsigned long), -1, sizeof(unsigned long), -1, 0, &u); 39 | } 40 | 41 | inline void mpz_to_uint256(mpz_t r, uint256 &u) 42 | { 43 | memset(&u, 0, sizeof(uint256)); 44 | mpz_export(&u, nullptr, -1, 4, 0, 0, r); 45 | } 46 | 47 | static double difficultyFromBits(int64_t nBits) 48 | { 49 | uint256 powLimit256("03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); 50 | uint32_t powLimit = powLimit256.GetCompact(false); 51 | int nShift = (nBits >> 24) & 0xff; 52 | int nShiftAmount = (powLimit >> 24) & 0xff; 53 | 54 | double dDiff = (double)(powLimit & 0x00ffffff) / (double)(nBits & 0x00ffffff); 55 | while (nShift < nShiftAmount) { 56 | dDiff *= 256.0; 57 | nShift++; 58 | } 59 | 60 | while (nShift > nShiftAmount) { 61 | dDiff /= 256.0; 62 | nShift--; 63 | } 64 | 65 | return dDiff;; 66 | } 67 | 68 | static mpz_class hashTargetFromBits(unsigned nBits) 69 | { 70 | mpz_class target; 71 | uint256 hashTarget; 72 | hashTarget = nBits & 0x007FFFFF; 73 | unsigned exponent = nBits >> 24; 74 | if (exponent <= 3) 75 | hashTarget >>= 8*(3-exponent); 76 | else 77 | hashTarget <<= 8*(exponent-3); 78 | mpz_from_uint256(target.get_mpz_t(), hashTarget); 79 | return target; 80 | } 81 | 82 | static void sigIntHandler(int c) 83 | { 84 | int msg = 0; 85 | write(gPoolContext->signalPipeFd.write, &msg, sizeof(msg)); 86 | } 87 | 88 | void sendSignalCb(AsyncOpStatus status, zmtpSocket *socket, void *arg) 89 | { 90 | poolContext *ctx = (poolContext*)arg; 91 | if (status == aosDisconnected) { 92 | for (auto I = ctx->signalSockets.begin(), IE = ctx->signalSockets.end(); I != IE; ++I) { 93 | if (*I == socket) { 94 | ctx->signalSockets.erase(I); 95 | break; 96 | } 97 | } 98 | 99 | zmtpSocketDelete(socket); 100 | } 101 | } 102 | 103 | void sendSignal(poolContext *ctx, const void *data, size_t size) 104 | { 105 | for (size_t i = 0; i < ctx->signalSockets.size(); i++) { 106 | aioZmtpSend(ctx->signalSockets[i], (void*)data, size, zmtpMessage, afNone, TM, sendSignalCb, ctx); 107 | } 108 | } 109 | 110 | void updateStratumWorkers(void *arg) 111 | { 112 | poolContext *ctx = (poolContext*)arg; 113 | for (auto &w: ctx->stratumWorkers) 114 | stratumSendNewWork(ctx, w.second.socket, w.first); 115 | } 116 | 117 | void frontendProc(void *arg) 118 | { 119 | readerContext *rctx = (readerContext*)arg; 120 | poolContext *poolCtx = rctx->poolCtx; 121 | zmtpSocket *socket = zmtpSocketNew(poolCtx->base, newSocketIo(poolCtx->base, rctx->socket), zmtpSocketDEALER); 122 | 123 | if (ioZmtpAccept(socket, afNone, TM) < 0) { 124 | zmtpSocketDelete(socket); 125 | return; 126 | } 127 | 128 | pool::proto::Request req; 129 | pool::proto::Reply rep; 130 | zmtpStream stream; 131 | zmtpUserMsgTy msgType; 132 | while ((ioZmtpRecv(socket, stream, 65536, afNone, 0, &msgType) > 0) && msgType == zmtpMessage) { 133 | if (checkRequest(poolCtx, req, rep, stream.data(), stream.remaining())) { 134 | pool::proto::Request::Type requestType = req.type(); 135 | 136 | // Valid requests here: 137 | // CONNECT 138 | if (requestType == pool::proto::Request::CONNECT) { 139 | onConnect(poolCtx, req, rep); 140 | } 141 | 142 | size_t repSize = rep.ByteSize(); 143 | stream.reset(); 144 | rep.SerializeToArray(stream.alloc(repSize), repSize); 145 | ioZmtpSend(socket, stream.data(), stream.sizeOf(), zmtpMessage, afNone, TM); 146 | } else { 147 | break; 148 | } 149 | } 150 | 151 | zmtpSocketDelete(socket); 152 | } 153 | 154 | void mainProc(void *arg) 155 | { 156 | readerContext *rctx = (readerContext*)arg; 157 | poolContext *poolCtx = rctx->poolCtx; 158 | zmtpSocket *socket = zmtpSocketNew(poolCtx->base, newSocketIo(poolCtx->base, rctx->socket), zmtpSocketROUTER); 159 | 160 | if (ioZmtpAccept(socket, afNone, TM) < 0) { 161 | zmtpSocketDelete(socket); 162 | return; 163 | } 164 | 165 | zmtpStream stream; 166 | pool::proto::Request req; 167 | pool::proto::Reply rep; 168 | zmtpUserMsgTy msgType; 169 | while ((ioZmtpRecv(socket, stream, 65536, afNone, 0, &msgType) > 0) && msgType == zmtpMessage) { 170 | if (checkRequest(poolCtx, req, rep, stream.data(), stream.remaining())) { 171 | pool::proto::Request::Type requestType = req.type(); 172 | 173 | // Valid requests here: 174 | // GETWORK 175 | // SHARE 176 | // STATS 177 | bool needDisconnect = false; 178 | if (requestType == pool::proto::Request::GETWORK) { 179 | onGetWork(poolCtx, req, rep, &needDisconnect); 180 | } else if (requestType == pool::proto::Request::SHARE) { 181 | onShare(poolCtx, req, rep, &needDisconnect); 182 | } else if (requestType == pool::proto::Request::STATS) { 183 | onStats(poolCtx, req, rep); 184 | } 185 | 186 | size_t repSize = rep.ByteSize(); 187 | stream.reset(); 188 | rep.SerializeToArray(stream.alloc(repSize), repSize); 189 | ioZmtpSend(socket, stream.data(), stream.sizeOf(), zmtpMessage, afNone, TM); 190 | if (needDisconnect) 191 | break; 192 | } else { 193 | break; 194 | } 195 | } 196 | 197 | zmtpSocketDelete(socket); 198 | } 199 | 200 | void stratumProc(void *arg) 201 | { 202 | readerContext *rctx = (readerContext*)arg; 203 | poolContext *poolCtx = rctx->poolCtx; 204 | aioObject *socket = newSocketIo(poolCtx->base, rctx->socket); 205 | 206 | int64_t sessionId = poolCtx->sessionId++; 207 | 208 | // TODO: lookup '\n' after message 209 | bool sessionActive = true; 210 | ssize_t bytesRead; 211 | ssize_t offset = 0; 212 | char *buffer = (char*)malloc(40960); 213 | while ( sessionActive && (bytesRead = ioRead(socket, buffer+offset, 40960 - offset - 1, afNone, 0)) > 0) { 214 | offset += bytesRead; 215 | buffer[offset] = 0; 216 | char *p = strchr(buffer, '\n'); 217 | if (!p) 218 | continue; 219 | 220 | *p = 0; 221 | 222 | StratumMessage msg; 223 | switch (decodeStratumMessage(buffer, &msg)) { 224 | case StratumDecodeStatusTy::Ok : 225 | // Process stratum messages here 226 | switch (msg.method) { 227 | case StratumMethodTy::Subscribe : 228 | onStratumSubscribe(poolCtx, socket, &msg, sessionId); 229 | break; 230 | case StratumMethodTy::Authorize : 231 | onStratumAuthorize(poolCtx, socket, &msg, sessionId); 232 | 233 | // send target and work 234 | stratumSendSetTarget(poolCtx, socket); 235 | stratumSendNewWork(poolCtx, socket, sessionId); 236 | break; 237 | case StratumMethodTy::ExtraNonceSubscribe : 238 | // nothing to do 239 | break; 240 | case StratumMethodTy::Submit : 241 | onStratumSubmit(poolCtx, socket, &msg, sessionId); 242 | break; 243 | default : 244 | // unknown method 245 | break; 246 | } 247 | 248 | break; 249 | case StratumDecodeStatusTy::JsonError : 250 | sessionActive = false; 251 | break; 252 | case StratumDecodeStatusTy::FormatError : 253 | break; 254 | default : 255 | break; 256 | } 257 | 258 | // move remaining to begin of buffer 259 | ssize_t nextMsgOffset = p+1-buffer; 260 | if (nextMsgOffset < offset) { 261 | memcpy(buffer, buffer+nextMsgOffset, offset-nextMsgOffset); 262 | offset = offset - nextMsgOffset; 263 | } else { 264 | offset = 0; 265 | } 266 | } 267 | 268 | poolCtx->stratumWorkers.erase(sessionId); 269 | free(buffer); 270 | deleteAioObject(socket); 271 | } 272 | 273 | 274 | void signalsProc(void *arg) 275 | { 276 | readerContext *rctx = (readerContext*)arg; 277 | poolContext *ctx = (poolContext*)rctx->poolCtx; 278 | zmtpSocket *socket = zmtpSocketNew(ctx->base, newSocketIo(ctx->base, rctx->socket), zmtpSocketPUB); 279 | 280 | zmtpStream stream; 281 | if (ioZmtpAccept(socket, afNone, TM) < 0) { 282 | zmtpSocketDelete(socket); 283 | return; 284 | } 285 | 286 | ctx->signalSockets.push_back(socket); 287 | } 288 | 289 | template 290 | void mpz_class_set(mpz_class &rop, Ty op) 291 | { 292 | mpz_import(rop.get_mpz_t(), 1, 1, sizeof(op), 0, 0, &op); 293 | } 294 | 295 | void timerProc(void *arg) 296 | { 297 | poolContext *ctx = (poolContext*)arg; 298 | 299 | aioUserEvent *timerEvent = newUserEvent(ctx->base, nullptr, nullptr); 300 | bool connectedBefore = ctx->client->connected(); 301 | xmstream stream; 302 | while (true) { 303 | ioSleep(timerEvent, 500000); 304 | if (connectedBefore != ctx->client->connected()) { 305 | if (connectedBefore == false) { 306 | auto receivedBlock = ioGetCurrentBlock(ctx->client); 307 | if (!receivedBlock) 308 | continue; 309 | ctx->difficulty = difficultyFromBits(receivedBlock->bits); 310 | ctx->extraNonceMap.clear(); 311 | ctx->mCurrBlock.set_height(receivedBlock->height); 312 | ctx->mCurrBlock.set_hash(receivedBlock->hash.c_str()); 313 | ctx->mCurrBlock.set_prevhash(receivedBlock->prevhash.c_str()); 314 | ctx->mCurrBlock.set_reqdiff(ctx->shareTargetBits); 315 | ctx->mCurrBlock.set_minshare(0); 316 | 317 | ctx->uniqueShares.clear(); 318 | ctx->stratumTaskMap.clear(); 319 | 320 | mpz_class blockTarget; 321 | mpz_class_set(blockTarget, receivedBlock->bits & 0x007FFFFF); 322 | unsigned exponent = receivedBlock->bits >> 24; 323 | if (exponent <= 3) 324 | blockTarget >>= 8*(3-exponent); 325 | else 326 | blockTarget <<= 8*(exponent-3); 327 | mpz_to_uint256(blockTarget.get_mpz_t(), ctx->blockTarget); 328 | 329 | mpz_class sharesPerBlock = ctx->shareTargetMpz / blockTarget; 330 | LOG_F(INFO, 331 | " * new block: %u, diff=%.5lf, approximate shares per block: %lu", 332 | (unsigned)receivedBlock->height, 333 | ctx->difficulty, 334 | std::max(sharesPerBlock.get_ui(), 1ul)); 335 | 336 | pool::proto::Signal sig; 337 | pool::proto::Block* block = sig.mutable_block(); 338 | 339 | 340 | } else { 341 | ctx->mCurrBlock.set_height(0); 342 | } 343 | 344 | pool::proto::Signal sig; 345 | pool::proto::Block* block = sig.mutable_block(); 346 | sig.set_type(pool::proto::Signal::NEWBLOCK); 347 | block->CopyFrom(ctx->mCurrBlock); 348 | stream.reset(); 349 | stream.write(1); 350 | size_t size = sig.ByteSize(); 351 | sig.SerializeToArray(stream.alloc(size), size); 352 | sendSignal(ctx, stream.data(), stream.offsetOf()); 353 | updateStratumWorkers(ctx); 354 | } 355 | 356 | connectedBefore = ctx->client->connected(); 357 | 358 | // update work for stratum miners if needed 359 | time_t tm = time(0); 360 | for (auto &w: ctx->stratumWorkers) { 361 | if (tm - w.second.lastUpdateTime >= ctx->stratumWorkLifeTime) 362 | stratumSendNewWork(ctx, w.second.socket, w.first); 363 | } 364 | } 365 | } 366 | 367 | 368 | void stratumStatsProc(void *arg) 369 | { 370 | poolContext *ctx = (poolContext*)arg; 371 | aioUserEvent *timerEvent = newUserEvent(ctx->base, nullptr, nullptr); 372 | while (true) { 373 | ioSleep(timerEvent, 60*1000000); 374 | 375 | for (auto &w: ctx->stratumWorkers) 376 | stratumSendStats(ctx, w.second); 377 | } 378 | } 379 | 380 | 381 | void listener(void *arg) 382 | { 383 | listenerContext *ctx = (listenerContext*)arg; 384 | while (true) { 385 | HostAddress address; 386 | socketTy acceptSocket = ioAccept(ctx->socket, 0); 387 | if (acceptSocket > 0) { 388 | readerContext *rctx = new readerContext; 389 | rctx->socket = acceptSocket; 390 | rctx->poolCtx = (poolContext*)ctx->arg; 391 | coroutineTy *proc = coroutineNew(ctx->proc, rctx, 0x40000); 392 | coroutineCall(proc); 393 | } 394 | } 395 | } 396 | 397 | 398 | aioObject *createListener(asyncBase *base, uint16_t port, coroutineProcTy proc, aioObject **socketPtr, void *arg) 399 | { 400 | HostAddress address; 401 | address.family = AF_INET; 402 | address.ipv4 = INADDR_ANY; 403 | address.port = htons(port); 404 | socketTy hSocket = socketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP, 1); 405 | socketReuseAddr(hSocket); 406 | if (socketBind(hSocket, &address) != 0) { 407 | LOG_F(ERROR, "cannot bind port: %i", port); 408 | exit(1); 409 | } 410 | 411 | if (socketListen(hSocket) != 0) { 412 | LOG_F(ERROR, "listen error: %i", port); 413 | exit(1); 414 | } 415 | 416 | aioObject *socket = newSocketIo(base, hSocket); 417 | listenerContext *ctx = new listenerContext; 418 | ctx->base = base; 419 | ctx->socket = socket; 420 | ctx->proc = proc; 421 | ctx->arg = arg; 422 | 423 | if (socketPtr) 424 | *socketPtr = socket; 425 | 426 | coroutineTy *listenerProc = coroutineNew(listener, ctx, 0x10000); 427 | coroutineCall(listenerProc); 428 | } 429 | 430 | void signalHandler(p2pPeer *peer, void *buffer, size_t size, void *arg) 431 | { 432 | poolContext *context = (poolContext*)arg; 433 | const Signal *signal = flatbuffers::GetRoot(buffer); 434 | xmstream stream; 435 | switch (signal->signalId()) { 436 | case SignalId_NewBlock : { 437 | const Block *receivedBlock = static_cast(signal->data()); 438 | context->difficulty = difficultyFromBits(receivedBlock->bits()); 439 | context->extraNonceMap.clear(); 440 | context->mCurrBlock.set_height(receivedBlock->height()); 441 | context->mCurrBlock.set_hash(receivedBlock->hash()->c_str()); 442 | context->mCurrBlock.set_prevhash(receivedBlock->prevhash()->c_str()); 443 | context->mCurrBlock.set_reqdiff(context->shareTargetBits); 444 | context->mCurrBlock.set_minshare(0); 445 | 446 | context->uniqueShares.clear(); 447 | context->stratumTaskMap.clear(); 448 | 449 | mpz_class blockTarget; 450 | mpz_class_set(blockTarget, receivedBlock->bits() & 0x007FFFFF); 451 | unsigned exponent = receivedBlock->bits() >> 24; 452 | if (exponent <= 3) 453 | blockTarget >>= 8*(3-exponent); 454 | else 455 | blockTarget <<= 8*(exponent-3); 456 | mpz_to_uint256(blockTarget.get_mpz_t(), context->blockTarget); 457 | 458 | mpz_class sharesPerBlock = context->shareTargetMpz / blockTarget; 459 | 460 | LOG_F(INFO, 461 | " * new block signal: %u, diff=%.5lf, approximate shares per block: %lu", 462 | (unsigned)receivedBlock->height(), 463 | context->difficulty, 464 | std::max(sharesPerBlock.get_ui(), 1ul)); 465 | 466 | pool::proto::Signal sig; 467 | pool::proto::Block* block = sig.mutable_block(); 468 | 469 | sig.set_type(pool::proto::Signal::NEWBLOCK); 470 | block->CopyFrom(context->mCurrBlock); 471 | 472 | stream.reset(); 473 | stream.write(1); 474 | size_t size = sig.ByteSize(); 475 | sig.SerializeToArray(stream.alloc(size), size); 476 | sendSignal(context, stream.data(), stream.offsetOf()); 477 | coroutineCall(coroutineNew(updateStratumWorkers, context, 0x10000)); 478 | } 479 | } 480 | } 481 | 482 | void sigintProc(void *arg) 483 | { 484 | int msg; 485 | poolContext *context = (poolContext*)arg; 486 | ioRead(context->signalReadObject, &msg, sizeof(msg), afWaitAll, 0); 487 | 488 | deleteAioObject(context->mainSocket); 489 | 490 | xmstream stream; 491 | pool::proto::Signal sig; 492 | sig.set_type(pool::proto::Signal_Type_SHUTDOWN); 493 | stream.reset(); 494 | stream.write(1); 495 | size_t size = sig.ByteSize(); 496 | sig.SerializeToArray(stream.alloc(size), size); 497 | sendSignal(context, stream.data(), stream.offsetOf()); 498 | 499 | context->backend->stop(); 500 | 501 | aioUserEvent *timerEvent = newUserEvent(context->base, nullptr, nullptr); 502 | printf("\n"); 503 | for (unsigned i = 0; i < 3; i++) { 504 | printf("."); 505 | fflush(stdout); 506 | ioSleep(timerEvent, 1000000); 507 | } 508 | printf("\n"); 509 | postQuitOperation(context->base); 510 | } 511 | 512 | static bool checkZECAddress(const char *address) 513 | { 514 | CZECAddress A(address); 515 | return A.isValid(); 516 | } 517 | 518 | int main(int argc, char **argv) 519 | { 520 | if (argc != 2) { 521 | fprintf(stderr, "Usage: %s \n", argv[0]); 522 | return 1; 523 | } 524 | 525 | char logFileName[64]; 526 | { 527 | auto t = std::time(nullptr); 528 | auto now = std::localtime(&t); 529 | snprintf(logFileName, sizeof(logFileName), "pool_frontend_zcash-%04u-%02u-%02u.log", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday); 530 | } 531 | 532 | loguru::g_stderr_verbosity = loguru::Verbosity_OFF; 533 | loguru::g_preamble_thread = false; 534 | loguru::g_preamble_file = false; 535 | loguru::g_flush_interval_ms = 100; 536 | loguru::init(argc, argv); 537 | loguru::add_file(logFileName, loguru::Append, loguru::Verbosity_INFO); 538 | loguru::g_stderr_verbosity = 1; 539 | 540 | PoolBackend::config backendConfig; 541 | poolContext context; 542 | bool checkAddress; 543 | uint16_t stratumPort; 544 | config4cpp::Configuration *cfg = config4cpp::Configuration::create(); 545 | 546 | try { 547 | cfg->parse(argv[1]); 548 | 549 | backendConfig.isMaster = cfg->lookupBoolean("pool_frontend_zcash", "isMaster", true); 550 | backendConfig.poolFee = cfg->lookupInt("pool_frontend_zcash", "poolFee", 0); 551 | backendConfig.poolFeeAddr = cfg->lookupString("pool_frontend_zcash", "poolFeeAddr", ""); 552 | 553 | 554 | config4cpp::StringVector wallets; 555 | cfg->lookupList("pool_frontend_zcash", "walletAddrs", wallets); 556 | for (decltype(wallets.length()) i = 0; i < wallets.length(); i++) { 557 | URI uri; 558 | if (!uriParse(wallets[i], &uri)) { 559 | LOG_F(ERROR, "can't read walletaddrs from configuration file"); 560 | return 1; 561 | } 562 | 563 | if (uri.schema != "p2p" || !uri.ipv4 || !uri.port) { 564 | LOG_F(ERROR, "walletaddrs can be contain only p2p://xxx.xxx.xxx.xxx:port address now"); 565 | return 1; 566 | } 567 | 568 | HostAddress address; 569 | address.family = AF_INET; 570 | address.ipv4 = uri.ipv4; 571 | address.port = xhton(uri.port); 572 | backendConfig.peers.push_back(address); 573 | } 574 | 575 | { 576 | URI uri; 577 | const char *localAddress = cfg->lookupString("pool_frontend_zcash", "localAddress"); 578 | if (!uriParse(localAddress, &uri)) { 579 | LOG_F(ERROR, "can't read localAddress from configuration file"); 580 | return 1; 581 | } 582 | 583 | if (uri.schema != "p2p" || !uri.ipv4 || !uri.port) { 584 | LOG_F(ERROR, "localAddress can be contain only p2p://xxx.xxx.xxx.xxx:port address now"); 585 | return 1; 586 | } 587 | 588 | HostAddress address; 589 | address.family = AF_INET; 590 | address.ipv4 = uri.ipv4; 591 | address.port = xhton(uri.port); 592 | backendConfig.listenAddress = address; 593 | } 594 | 595 | checkAddress = cfg->lookupBoolean("pool_frontend_zcash", "checkAddress", true); 596 | 597 | backendConfig.walletAppName = cfg->lookupString("pool_frontend_zcash", "walletAppName", "pool_rpc"); 598 | backendConfig.poolAppName = cfg->lookupString("pool_frontend_zcash", "poolAppName", "pool_frontend_zcash"); 599 | backendConfig.requiredConfirmations = cfg->lookupInt("pool_frontend_zcash", "requiredConfirmations", 10); 600 | backendConfig.defaultMinimalPayout = (int64_t)(cfg->lookupFloat("pool_frontend_zcash", "defaultMinimalPayout", 4)*COIN); 601 | backendConfig.minimalPayout = (int64_t)(cfg->lookupFloat("pool_frontend_zcash", "minimalPayout", 0.001)*COIN); 602 | backendConfig.dbPath = cfg->lookupString("pool_frontend_zcash", "dbPath"); 603 | backendConfig.keepRoundTime = cfg->lookupInt("pool_frontend_zcash", "keepRoundTime", 3) * 24*3600; 604 | backendConfig.keepStatsTime = cfg->lookupInt("pool_frontend_zcash", "keepStatsTime", 2) * 60; 605 | backendConfig.confirmationsCheckInterval = cfg->lookupInt("pool_frontend_zcash", "confirmationsCheckInterval", 10) * 60 * 1000000; 606 | backendConfig.payoutInterval = cfg->lookupInt("pool_frontend_zcash", "payoutInterval", 60) * 60 * 1000000; 607 | backendConfig.balanceCheckInterval = cfg->lookupInt("pool_frontend_zcash", "balanceCheckInterval", 3) * 60 * 1000000; 608 | backendConfig.statisticCheckInterval = cfg->lookupInt("pool_frontend_zcash", "statisticCheckInterval", 1) * 60 * 1000000; 609 | 610 | backendConfig.checkAddressProc = checkAddress ? checkZECAddress : 0; 611 | backendConfig.useAsyncPayout = true; 612 | backendConfig.poolZAddr = cfg->lookupString("pool_frontend_zcash", "pool_zaddr"); 613 | backendConfig.poolTAddr = cfg->lookupString("pool_frontend_zcash", "pool_taddr"); 614 | 615 | context.xpmclientHost = cfg->lookupString("pool_frontend_zcash", "zmqclientHost"); 616 | context.xpmclientListenPort = cfg->lookupInt("pool_frontend_zcash", "zmqclientListenPort"); 617 | context.xpmclientWorkPort = cfg->lookupInt("pool_frontend_zcash", "zmqclientWorkPort"); 618 | context.stratumWorkLifeTime = cfg->lookupInt("pool_frontend_zcash", "stratumWorkLifeTime", 9) * 60; 619 | stratumPort = cfg->lookupInt("pool_frontend_zcash", "stratumPort", 3357); 620 | 621 | // calculate share target 622 | context.shareTargetCoeff = cfg->lookupInt("pool_frontend_zcash", "shareTarget", 1024); 623 | mpz_class N = 1; 624 | N <<= 256; 625 | N /= context.shareTargetCoeff; 626 | mpz_to_uint256(N.get_mpz_t(), context.shareTarget); 627 | context.shareTargetBits = context.shareTarget.GetCompact(false); 628 | context.shareTargetMpz = N; 629 | context.shareTargetForStratum = context.shareTarget.ToString(); 630 | LOG_F(INFO, "share target for stratum is %s", context.shareTargetForStratum.c_str()); 631 | 632 | context.equihashShareCheck = cfg->lookupBoolean("pool_frontend_zcash", "equihashShareCheck", true); 633 | } catch(const config4cpp::ConfigurationException& ex){ 634 | LOG_F(ERROR, "%s", ex.c_str()); 635 | exit(1); 636 | } 637 | 638 | initializeSocketSubsystem(); 639 | 640 | asyncBase *base = createAsyncBase(amOSDefault); 641 | 642 | 643 | context.base = base; 644 | context.mCurrBlock.set_height(0); 645 | 646 | // ZMQ protocol 647 | createListener(base, context.xpmclientListenPort, frontendProc, &context.mainSocket, &context); 648 | createListener(base, context.xpmclientWorkPort, mainProc, nullptr, &context); 649 | createListener(base, context.xpmclientWorkPort+1, signalsProc, nullptr, &context); 650 | 651 | // Stratum protocol 652 | context.checkAddress = checkAddress; 653 | context.sessionId = 0; 654 | createListener(base, stratumPort, stratumProc, nullptr, &context); 655 | coroutineCall(coroutineNew(stratumStatsProc, &context, 0x10000)); 656 | 657 | context.client = 658 | p2pNode::createClient(base, 659 | &backendConfig.peers[0], 660 | backendConfig.peers.size(), 661 | backendConfig.walletAppName.c_str()); 662 | 663 | context.client->setSignalHandler(signalHandler, &context); 664 | 665 | coroutineCall(coroutineNew(timerProc, &context, 0x10000)); 666 | 667 | 668 | context.backend = new PoolBackend(&backendConfig); 669 | context.backend->start(); 670 | 671 | // Handle CTRL+C (SIGINT) 672 | { 673 | gPoolContext = &context; 674 | pipeCreate(&context.signalPipeFd, 1); 675 | context.signalReadObject = newDeviceIo(base, context.signalPipeFd.read); 676 | context.signalWriteObject = newDeviceIo(base, context.signalPipeFd.write); 677 | signal(SIGINT, sigIntHandler); 678 | coroutineCall(coroutineNew(sigintProc, &context, 0x10000)); 679 | } 680 | 681 | asyncLoop(base); 682 | LOG_F(INFO, "pool_frondend_zcash stopped\n"); 683 | return 0; 684 | } 685 | -------------------------------------------------------------------------------- /src/uint256.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto 2 | // Copyright (c) 2009-2012 The Bitcoin developers 3 | // Distributed under the MIT/X11 software license, see the accompanying 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | #ifndef BITCOIN_UINT256_H 6 | #define BITCOIN_UINT256_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | typedef long long int64; 16 | typedef unsigned long long uint64; 17 | 18 | 19 | inline int Testuint256AdHoc(std::vector vArg); 20 | 21 | 22 | 23 | /** Base class without constructors for uint256 and uint160. 24 | * This makes the compiler let you use it in a union. 25 | */ 26 | template 27 | class base_uint 28 | { 29 | protected: 30 | enum { WIDTH=BITS/32 }; 31 | uint32_t pn[WIDTH]; 32 | public: 33 | 34 | bool operator!() const 35 | { 36 | for (int i = 0; i < WIDTH; i++) 37 | if (pn[i] != 0) 38 | return false; 39 | return true; 40 | } 41 | 42 | const base_uint operator~() const 43 | { 44 | base_uint ret; 45 | for (int i = 0; i < WIDTH; i++) 46 | ret.pn[i] = ~pn[i]; 47 | return ret; 48 | } 49 | 50 | const base_uint operator-() const 51 | { 52 | base_uint ret; 53 | for (int i = 0; i < WIDTH; i++) 54 | ret.pn[i] = ~pn[i]; 55 | ret++; 56 | return ret; 57 | } 58 | 59 | double getdouble() const 60 | { 61 | double ret = 0.0; 62 | double fact = 1.0; 63 | for (int i = 0; i < WIDTH; i++) { 64 | ret += fact * pn[i]; 65 | fact *= 4294967296.0; 66 | } 67 | return ret; 68 | } 69 | 70 | base_uint& operator=(uint64 b) 71 | { 72 | pn[0] = (unsigned int)b; 73 | pn[1] = (unsigned int)(b >> 32); 74 | for (int i = 2; i < WIDTH; i++) 75 | pn[i] = 0; 76 | return *this; 77 | } 78 | 79 | base_uint& operator^=(const base_uint& b) 80 | { 81 | for (int i = 0; i < WIDTH; i++) 82 | pn[i] ^= b.pn[i]; 83 | return *this; 84 | } 85 | 86 | base_uint& operator&=(const base_uint& b) 87 | { 88 | for (int i = 0; i < WIDTH; i++) 89 | pn[i] &= b.pn[i]; 90 | return *this; 91 | } 92 | 93 | base_uint& operator|=(const base_uint& b) 94 | { 95 | for (int i = 0; i < WIDTH; i++) 96 | pn[i] |= b.pn[i]; 97 | return *this; 98 | } 99 | 100 | base_uint& operator^=(uint64 b) 101 | { 102 | pn[0] ^= (unsigned int)b; 103 | pn[1] ^= (unsigned int)(b >> 32); 104 | return *this; 105 | } 106 | 107 | base_uint& operator|=(uint64 b) 108 | { 109 | pn[0] |= (unsigned int)b; 110 | pn[1] |= (unsigned int)(b >> 32); 111 | return *this; 112 | } 113 | 114 | base_uint& operator<<=(unsigned int shift) 115 | { 116 | base_uint a(*this); 117 | for (int i = 0; i < WIDTH; i++) 118 | pn[i] = 0; 119 | int k = shift / 32; 120 | shift = shift % 32; 121 | for (int i = 0; i < WIDTH; i++) 122 | { 123 | if (i+k+1 < WIDTH && shift != 0) 124 | pn[i+k+1] |= (a.pn[i] >> (32-shift)); 125 | if (i+k < WIDTH) 126 | pn[i+k] |= (a.pn[i] << shift); 127 | } 128 | return *this; 129 | } 130 | 131 | base_uint& operator>>=(unsigned int shift) 132 | { 133 | base_uint a(*this); 134 | for (int i = 0; i < WIDTH; i++) 135 | pn[i] = 0; 136 | int k = shift / 32; 137 | shift = shift % 32; 138 | for (int i = 0; i < WIDTH; i++) 139 | { 140 | if (i-k-1 >= 0 && shift != 0) 141 | pn[i-k-1] |= (a.pn[i] << (32-shift)); 142 | if (i-k >= 0) 143 | pn[i-k] |= (a.pn[i] >> shift); 144 | } 145 | return *this; 146 | } 147 | 148 | base_uint& operator+=(const base_uint& b) 149 | { 150 | uint64 carry = 0; 151 | for (int i = 0; i < WIDTH; i++) 152 | { 153 | uint64 n = carry + pn[i] + b.pn[i]; 154 | pn[i] = n & 0xffffffff; 155 | carry = n >> 32; 156 | } 157 | return *this; 158 | } 159 | 160 | base_uint& operator-=(const base_uint& b) 161 | { 162 | *this += -b; 163 | return *this; 164 | } 165 | 166 | base_uint& operator+=(uint64 b64) 167 | { 168 | base_uint b; 169 | b = b64; 170 | *this += b; 171 | return *this; 172 | } 173 | 174 | base_uint& operator-=(uint64 b64) 175 | { 176 | base_uint b; 177 | b = b64; 178 | *this += -b; 179 | return *this; 180 | } 181 | 182 | 183 | base_uint& operator++() 184 | { 185 | // prefix operator 186 | int i = 0; 187 | while (++pn[i] == 0 && i < WIDTH-1) 188 | i++; 189 | return *this; 190 | } 191 | 192 | const base_uint operator++(int) 193 | { 194 | // postfix operator 195 | const base_uint ret = *this; 196 | ++(*this); 197 | return ret; 198 | } 199 | 200 | base_uint& operator--() 201 | { 202 | // prefix operator 203 | int i = 0; 204 | while (--pn[i] == -1 && i < WIDTH-1) 205 | i++; 206 | return *this; 207 | } 208 | 209 | const base_uint operator--(int) 210 | { 211 | // postfix operator 212 | const base_uint ret = *this; 213 | --(*this); 214 | return ret; 215 | } 216 | 217 | 218 | friend inline bool operator<(const base_uint& a, const base_uint& b) 219 | { 220 | for (int i = base_uint::WIDTH-1; i >= 0; i--) 221 | { 222 | if (a.pn[i] < b.pn[i]) 223 | return true; 224 | else if (a.pn[i] > b.pn[i]) 225 | return false; 226 | } 227 | return false; 228 | } 229 | 230 | friend inline bool operator<=(const base_uint& a, const base_uint& b) 231 | { 232 | for (int i = base_uint::WIDTH-1; i >= 0; i--) 233 | { 234 | if (a.pn[i] < b.pn[i]) 235 | return true; 236 | else if (a.pn[i] > b.pn[i]) 237 | return false; 238 | } 239 | return true; 240 | } 241 | 242 | friend inline bool operator>(const base_uint& a, const base_uint& b) 243 | { 244 | for (int i = base_uint::WIDTH-1; i >= 0; i--) 245 | { 246 | if (a.pn[i] > b.pn[i]) 247 | return true; 248 | else if (a.pn[i] < b.pn[i]) 249 | return false; 250 | } 251 | return false; 252 | } 253 | 254 | friend inline bool operator>=(const base_uint& a, const base_uint& b) 255 | { 256 | for (int i = base_uint::WIDTH-1; i >= 0; i--) 257 | { 258 | if (a.pn[i] > b.pn[i]) 259 | return true; 260 | else if (a.pn[i] < b.pn[i]) 261 | return false; 262 | } 263 | return true; 264 | } 265 | 266 | friend inline bool operator==(const base_uint& a, const base_uint& b) 267 | { 268 | for (int i = 0; i < base_uint::WIDTH; i++) 269 | if (a.pn[i] != b.pn[i]) 270 | return false; 271 | return true; 272 | } 273 | 274 | friend inline bool operator==(const base_uint& a, uint64 b) 275 | { 276 | if (a.pn[0] != (unsigned int)b) 277 | return false; 278 | if (a.pn[1] != (unsigned int)(b >> 32)) 279 | return false; 280 | for (int i = 2; i < base_uint::WIDTH; i++) 281 | if (a.pn[i] != 0) 282 | return false; 283 | return true; 284 | } 285 | 286 | friend inline bool operator!=(const base_uint& a, const base_uint& b) 287 | { 288 | return (!(a == b)); 289 | } 290 | 291 | friend inline bool operator!=(const base_uint& a, uint64 b) 292 | { 293 | return (!(a == b)); 294 | } 295 | 296 | 297 | 298 | std::string GetHex() const 299 | { 300 | char psz[sizeof(pn)*2 + 1]; 301 | for (unsigned int i = 0; i < sizeof(pn); i++) 302 | sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); 303 | return std::string(psz, psz + sizeof(pn)*2); 304 | } 305 | 306 | void SetHex(const char* psz) 307 | { 308 | for (int i = 0; i < WIDTH; i++) 309 | pn[i] = 0; 310 | 311 | // skip leading spaces 312 | while (isspace(*psz)) 313 | psz++; 314 | 315 | // skip 0x 316 | if (psz[0] == '0' && tolower(psz[1]) == 'x') 317 | psz += 2; 318 | 319 | // hex string to uint 320 | static const unsigned char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; 321 | const char* pbegin = psz; 322 | while (phexdigit[(unsigned char)*psz] || *psz == '0') 323 | psz++; 324 | psz--; 325 | unsigned char* p1 = (unsigned char*)pn; 326 | unsigned char* pend = p1 + WIDTH * 4; 327 | while (psz >= pbegin && p1 < pend) 328 | { 329 | *p1 = phexdigit[(unsigned char)*psz--]; 330 | if (psz >= pbegin) 331 | { 332 | *p1 |= (phexdigit[(unsigned char)*psz--] << 4); 333 | p1++; 334 | } 335 | } 336 | } 337 | 338 | void SetHex(const std::string& str) 339 | { 340 | SetHex(str.c_str()); 341 | } 342 | 343 | std::string ToString() const 344 | { 345 | return (GetHex()); 346 | } 347 | 348 | unsigned char* begin() 349 | { 350 | return (unsigned char*)&pn[0]; 351 | } 352 | 353 | unsigned char* end() 354 | { 355 | return (unsigned char*)&pn[WIDTH]; 356 | } 357 | 358 | const unsigned char* begin() const 359 | { 360 | return (unsigned char*)&pn[0]; 361 | } 362 | 363 | const unsigned char* end() const 364 | { 365 | return (unsigned char*)&pn[WIDTH]; 366 | } 367 | 368 | unsigned int size() const 369 | { 370 | return sizeof(pn); 371 | } 372 | 373 | uint64 Get64(int n=0) const 374 | { 375 | return pn[2*n] | (uint64)pn[2*n+1] << 32; 376 | } 377 | 378 | // unsigned int GetSerializeSize(int nType=0, int nVersion=PROTOCOL_VERSION) const 379 | unsigned int GetSerializeSize(int nType, int nVersion) const 380 | { 381 | return sizeof(pn); 382 | } 383 | 384 | template 385 | // void Serialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) const 386 | void Serialize(Stream& s, int nType, int nVersion) const 387 | { 388 | s.write((char*)pn, sizeof(pn)); 389 | } 390 | 391 | template 392 | // void Unserialize(Stream& s, int nType=0, int nVersion=PROTOCOL_VERSION) 393 | void Unserialize(Stream& s, int nType, int nVersion) 394 | { 395 | s.read((char*)pn, sizeof(pn)); 396 | } 397 | 398 | 399 | friend class uint160; 400 | friend class uint256; 401 | friend inline int Testuint256AdHoc(std::vector vArg); 402 | }; 403 | 404 | typedef base_uint<160> base_uint160; 405 | typedef base_uint<256> base_uint256; 406 | 407 | 408 | 409 | // 410 | // uint160 and uint256 could be implemented as templates, but to keep 411 | // compile errors and debugging cleaner, they're copy and pasted. 412 | // 413 | 414 | 415 | 416 | ////////////////////////////////////////////////////////////////////////////// 417 | // 418 | // uint160 419 | // 420 | 421 | /** 160-bit unsigned integer */ 422 | class uint160 : public base_uint160 423 | { 424 | public: 425 | typedef base_uint160 basetype; 426 | 427 | uint160() 428 | { 429 | for (int i = 0; i < WIDTH; i++) 430 | pn[i] = 0; 431 | } 432 | 433 | uint160(const basetype& b) 434 | { 435 | for (int i = 0; i < WIDTH; i++) 436 | pn[i] = b.pn[i]; 437 | } 438 | 439 | uint160& operator=(const basetype& b) 440 | { 441 | for (int i = 0; i < WIDTH; i++) 442 | pn[i] = b.pn[i]; 443 | return *this; 444 | } 445 | 446 | uint160(uint64 b) 447 | { 448 | pn[0] = (unsigned int)b; 449 | pn[1] = (unsigned int)(b >> 32); 450 | for (int i = 2; i < WIDTH; i++) 451 | pn[i] = 0; 452 | } 453 | 454 | uint160& operator=(uint64 b) 455 | { 456 | pn[0] = (unsigned int)b; 457 | pn[1] = (unsigned int)(b >> 32); 458 | for (int i = 2; i < WIDTH; i++) 459 | pn[i] = 0; 460 | return *this; 461 | } 462 | 463 | explicit uint160(const std::string& str) 464 | { 465 | SetHex(str); 466 | } 467 | 468 | explicit uint160(const std::vector& vch) 469 | { 470 | if (vch.size() == sizeof(pn)) 471 | memcpy(pn, &vch[0], sizeof(pn)); 472 | else 473 | *this = 0; 474 | } 475 | }; 476 | 477 | inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } 478 | inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } 479 | inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } 480 | inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } 481 | inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } 482 | inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; } 483 | 484 | inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; } 485 | inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; } 486 | inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; } 487 | inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } 488 | inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } 489 | 490 | inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } 491 | inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } 492 | inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } 493 | inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } 494 | inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } 495 | inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } 496 | inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } 497 | inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } 498 | inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } 499 | inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } 500 | inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } 501 | 502 | inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } 503 | inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } 504 | inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } 505 | inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } 506 | inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } 507 | inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } 508 | inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } 509 | inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } 510 | inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } 511 | inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } 512 | inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } 513 | 514 | inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } 515 | inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } 516 | inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } 517 | inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } 518 | inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } 519 | inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } 520 | inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } 521 | inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } 522 | inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } 523 | inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } 524 | inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } 525 | 526 | 527 | 528 | 529 | 530 | 531 | ////////////////////////////////////////////////////////////////////////////// 532 | // 533 | // uint256 534 | // 535 | 536 | /** 256-bit unsigned integer */ 537 | class uint256 : public base_uint256 538 | { 539 | public: 540 | typedef base_uint256 basetype; 541 | 542 | uint256() 543 | { 544 | for (int i = 0; i < WIDTH; i++) 545 | pn[i] = 0; 546 | } 547 | 548 | uint256(const basetype& b) 549 | { 550 | for (int i = 0; i < WIDTH; i++) 551 | pn[i] = b.pn[i]; 552 | } 553 | 554 | uint256& operator=(const basetype& b) 555 | { 556 | for (int i = 0; i < WIDTH; i++) 557 | pn[i] = b.pn[i]; 558 | return *this; 559 | } 560 | 561 | uint256(uint64 b) 562 | { 563 | pn[0] = (unsigned int)b; 564 | pn[1] = (unsigned int)(b >> 32); 565 | for (int i = 2; i < WIDTH; i++) 566 | pn[i] = 0; 567 | } 568 | 569 | uint256& operator=(uint64 b) 570 | { 571 | pn[0] = (unsigned int)b; 572 | pn[1] = (unsigned int)(b >> 32); 573 | for (int i = 2; i < WIDTH; i++) 574 | pn[i] = 0; 575 | return *this; 576 | } 577 | 578 | explicit uint256(const std::string& str) 579 | { 580 | SetHex(str); 581 | } 582 | 583 | explicit uint256(const std::vector& vch) 584 | { 585 | if (vch.size() == sizeof(pn)) 586 | memcpy(pn, &vch[0], sizeof(pn)); 587 | else 588 | *this = 0; 589 | } 590 | 591 | friend inline const uint256 operator<<(const uint256& a, unsigned int shift); 592 | friend inline const uint256 operator>>(const uint256& a, unsigned int shift); 593 | 594 | uint32_t GetCompact(bool fNegative) const { 595 | int nSize = (256 + 7) / 8; 596 | uint32_t nCompact = 0; 597 | if (nSize <= 3) { 598 | nCompact = Get64() << 8 * (3 - nSize); 599 | } else { 600 | uint256 bn = *this >> 8u * (nSize - 3); 601 | nCompact = bn.Get64(); 602 | } 603 | // The 0x00800000 bit denotes the sign. 604 | // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. 605 | if (nCompact & 0x00800000) { 606 | nCompact >>= 8; 607 | nSize++; 608 | } 609 | assert((nCompact & ~0x007fffff) == 0); 610 | assert(nSize < 256); 611 | nCompact |= nSize << 24; 612 | nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0); 613 | return nCompact; 614 | } 615 | }; 616 | 617 | inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } 618 | inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } 619 | inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } 620 | inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } 621 | inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } 622 | inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; } 623 | 624 | inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; } 625 | inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; } 626 | inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; } 627 | inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; } 628 | inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; } 629 | 630 | inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } 631 | inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } 632 | inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } 633 | inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } 634 | inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } 635 | inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } 636 | inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } 637 | inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } 638 | inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } 639 | inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } 640 | inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } 641 | 642 | inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; } 643 | inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; } 644 | inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; } 645 | inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; } 646 | inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; } 647 | inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; } 648 | inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; } 649 | inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; } 650 | inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; } 651 | inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; } 652 | inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; } 653 | 654 | inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } 655 | inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } 656 | inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } 657 | inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } 658 | inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } 659 | inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } 660 | inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } 661 | inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } 662 | inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } 663 | inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } 664 | inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | #ifdef TEST_UINT256 676 | 677 | inline int Testuint256AdHoc(std::vector vArg) 678 | { 679 | uint256 g(0); 680 | 681 | 682 | printf("%s\n", g.ToString().c_str()); 683 | g--; printf("g--\n"); 684 | printf("%s\n", g.ToString().c_str()); 685 | g--; printf("g--\n"); 686 | printf("%s\n", g.ToString().c_str()); 687 | g++; printf("g++\n"); 688 | printf("%s\n", g.ToString().c_str()); 689 | g++; printf("g++\n"); 690 | printf("%s\n", g.ToString().c_str()); 691 | g++; printf("g++\n"); 692 | printf("%s\n", g.ToString().c_str()); 693 | g++; printf("g++\n"); 694 | printf("%s\n", g.ToString().c_str()); 695 | 696 | 697 | 698 | uint256 a(7); 699 | printf("a=7\n"); 700 | printf("%s\n", a.ToString().c_str()); 701 | 702 | uint256 b; 703 | printf("b undefined\n"); 704 | printf("%s\n", b.ToString().c_str()); 705 | int c = 3; 706 | 707 | a = c; 708 | a.pn[3] = 15; 709 | printf("%s\n", a.ToString().c_str()); 710 | uint256 k(c); 711 | 712 | a = 5; 713 | a.pn[3] = 15; 714 | printf("%s\n", a.ToString().c_str()); 715 | b = 1; 716 | b <<= 52; 717 | 718 | a |= b; 719 | 720 | a ^= 0x500; 721 | 722 | printf("a %s\n", a.ToString().c_str()); 723 | 724 | a = a | b | (uint256)0x1000; 725 | 726 | 727 | printf("a %s\n", a.ToString().c_str()); 728 | printf("b %s\n", b.ToString().c_str()); 729 | 730 | a = 0xfffffffe; 731 | a.pn[4] = 9; 732 | 733 | printf("%s\n", a.ToString().c_str()); 734 | a++; 735 | printf("%s\n", a.ToString().c_str()); 736 | a++; 737 | printf("%s\n", a.ToString().c_str()); 738 | a++; 739 | printf("%s\n", a.ToString().c_str()); 740 | a++; 741 | printf("%s\n", a.ToString().c_str()); 742 | 743 | a--; 744 | printf("%s\n", a.ToString().c_str()); 745 | a--; 746 | printf("%s\n", a.ToString().c_str()); 747 | a--; 748 | printf("%s\n", a.ToString().c_str()); 749 | uint256 d = a--; 750 | printf("%s\n", d.ToString().c_str()); 751 | printf("%s\n", a.ToString().c_str()); 752 | a--; 753 | printf("%s\n", a.ToString().c_str()); 754 | a--; 755 | printf("%s\n", a.ToString().c_str()); 756 | 757 | d = a; 758 | 759 | printf("%s\n", d.ToString().c_str()); 760 | for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); 761 | 762 | uint256 neg = d; 763 | neg = ~neg; 764 | printf("%s\n", neg.ToString().c_str()); 765 | 766 | 767 | uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); 768 | printf("\n"); 769 | printf("%s\n", e.ToString().c_str()); 770 | 771 | 772 | printf("\n"); 773 | uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); 774 | uint256 x2; 775 | printf("%s\n", x1.ToString().c_str()); 776 | for (int i = 0; i < 270; i += 4) 777 | { 778 | x2 = x1 << i; 779 | printf("%s\n", x2.ToString().c_str()); 780 | } 781 | 782 | printf("\n"); 783 | printf("%s\n", x1.ToString().c_str()); 784 | for (int i = 0; i < 270; i += 4) 785 | { 786 | x2 = x1; 787 | x2 >>= i; 788 | printf("%s\n", x2.ToString().c_str()); 789 | } 790 | 791 | 792 | for (int i = 0; i < 100; i++) 793 | { 794 | uint256 k = (~uint256(0) >> i); 795 | printf("%s\n", k.ToString().c_str()); 796 | } 797 | 798 | for (int i = 0; i < 100; i++) 799 | { 800 | uint256 k = (~uint256(0) << i); 801 | printf("%s\n", k.ToString().c_str()); 802 | } 803 | 804 | return (0); 805 | } 806 | 807 | #endif 808 | 809 | #endif 810 | --------------------------------------------------------------------------------