Browse Source

We are back

peercoin
Intel 10 years ago
parent
commit
6432acff9d
  1. 2
      .gitignore
  2. 4
      CMakeLists.txt
  3. 8
      src/server/poolserver/CMakeLists.txt
  4. 36
      src/server/poolserver/DataMgr/DataMgr.cpp
  5. 47
      src/server/poolserver/DataMgr/DataMgr.h
  6. 22
      src/server/poolserver/DataMgr/Share.h
  7. 15
      src/server/poolserver/Main.cpp
  8. 188
      src/server/poolserver/NetworkMgr/NetworkMgr.cpp
  9. 84
      src/server/poolserver/NetworkMgr/NetworkMgr.h
  10. 76
      src/server/poolserver/Server/Server.cpp
  11. 6
      src/server/poolserver/Server/Server.h
  12. 78
      src/server/poolserver/Stratum/Client.cpp
  13. 48
      src/server/poolserver/Stratum/Client.h
  14. 1
      src/server/poolserver/Stratum/Job.h
  15. 27
      src/server/poolserver/Stratum/Server.cpp
  16. 146
      src/server/poolserver/Stratum/Server.h
  17. 39
      src/server/poolserver/Stratum/ShareLimiter.cpp
  18. 44
      src/server/poolserver/Stratum/ShareLimiter.h
  19. 6
      src/server/poolserver/poolserver.cfg.dist
  20. 29
      src/server/shared/Bitcoin/Bitcoin.h
  21. 2
      src/server/shared/ByteBuffer.h
  22. 5
      src/server/shared/Common.h
  23. 27
      src/server/shared/Exception.h
  24. 34
      src/server/shared/JSON/JSON.h
  25. 3
      src/server/shared/JSON/JSONReader.h
  26. 35
      src/server/shared/JSONRPC/JSONRPC.cpp
  27. 9
      src/server/shared/JSONRPC/JSONRPC.h
  28. 10
      src/server/shared/MySQL/DatabaseConnection.cpp
  29. 15
      src/server/shared/MySQL/DatabaseConnection.h
  30. 20
      src/server/shared/Util.cpp
  31. 3
      src/server/shared/Util.h

2
.gitignore vendored

@ -15,3 +15,5 @@ CMakeLists.txt.user
*.BACKUP.* *.BACKUP.*
*.BASE.* *.BASE.*
*.LOCAL.* *.LOCAL.*
bin/
etc/

4
CMakeLists.txt

@ -25,10 +25,10 @@ set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF) set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_ALL_DYN_LINK ON) set(Boost_ALL_DYN_LINK ON)
SET(Boost_ADDITIONAL_VERSIONS "1.54" "1.54.0") SET(Boost_ADDITIONAL_VERSIONS "1.49" "1.49.0")
# Boost # Boost
find_package(Boost 1.54 COMPONENTS thread chrono program_options date_time REQUIRED) find_package(Boost 1.49 COMPONENTS thread chrono program_options date_time system REQUIRED)
message(status "** Boost Include: ${Boost_INCLUDE_DIR}") message(status "** Boost Include: ${Boost_INCLUDE_DIR}")
message(status "** Boost Libraries: ${Boost_LIBRARY_DIRS}") message(status "** Boost Libraries: ${Boost_LIBRARY_DIRS}")
message(status "** Boost Libraries: ${Boost_LIBRARIES}") message(status "** Boost Libraries: ${Boost_LIBRARIES}")

8
src/server/poolserver/CMakeLists.txt

@ -3,12 +3,16 @@
file(GLOB_RECURSE sources_Server Server/*.cpp Server/*.h) file(GLOB_RECURSE sources_Server Server/*.cpp Server/*.h)
file(GLOB_RECURSE sources_Database Database/*.cpp Database/*.h) file(GLOB_RECURSE sources_Database Database/*.cpp Database/*.h)
file(GLOB_RECURSE sources_Stratum Stratum/*.cpp Stratum/*.h) file(GLOB_RECURSE sources_Stratum Stratum/*.cpp Stratum/*.h)
file(GLOB_RECURSE sources_DataMgr DataMgr/*.cpp DataMgr/*.h)
file(GLOB_RECURSE sources_NetworkMgr NetworkMgr/*.cpp NetworkMgr/*.h)
file(GLOB sources_localdir *.cpp *.h) file(GLOB sources_localdir *.cpp *.h)
set(sources_Poolserver set(sources_Poolserver
${sources_Server} ${sources_Server}
${sources_Database} ${sources_Database}
${sources_Stratum} ${sources_Stratum}
${sources_DataMgr}
${sources_NetworkMgr}
${sources_localdir} ${sources_localdir}
) )
@ -16,6 +20,8 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/Server ${CMAKE_CURRENT_SOURCE_DIR}/Server
${CMAKE_CURRENT_SOURCE_DIR}/Database ${CMAKE_CURRENT_SOURCE_DIR}/Database
${CMAKE_CURRENT_SOURCE_DIR}/DataMgr
${CMAKE_CURRENT_SOURCE_DIR}/NetworkMgr
${CMAKE_SOURCE_DIR}/src/server/shared ${CMAKE_SOURCE_DIR}/src/server/shared
${CMAKE_SOURCE_DIR}/src/server/shared/Configuration ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
${CMAKE_SOURCE_DIR}/src/server/shared/MySQL ${CMAKE_SOURCE_DIR}/src/server/shared/MySQL
@ -46,7 +52,7 @@ target_link_libraries(poolserver
# Install # Install
if(UNIX) if(UNIX)
install(TARGETS poolserver DESTINATION bin) install(TARGETS poolserver DESTINATION bin)
install(FILES poolserver.cfg.dist DESTINATION bin) install(FILES poolserver.cfg.dist DESTINATION etc)
elseif(WIN32) elseif(WIN32)
install(TARGETS poolserver DESTINATION "${CMAKE_INSTALL_PREFIX}") install(TARGETS poolserver DESTINATION "${CMAKE_INSTALL_PREFIX}")
install(FILES poolserver.cfg.dist DESTINATION "${CMAKE_INSTALL_PREFIX}") install(FILES poolserver.cfg.dist DESTINATION "${CMAKE_INSTALL_PREFIX}")

36
src/server/poolserver/DataMgr/DataMgr.cpp

@ -0,0 +1,36 @@
#include "DataMgr.h"
#include "ServerDatabaseEnv.h"
#include "Util.h"
#include "Log.h"
template<>
void DataMgr<Share>::Upload()
{
sLog.Info(LOG_SERVER, "We have %u shares", Size());
while (Size() > BULK_MIN)
{
sLog.Info(LOG_SERVER, "Uploading %u shares to database", Size());
std::string query("INSERT INTO `shares` (`rem_host`, `username`, `our_result`, `upstream_result`, `reason`, `time`, `difficulty`) VALUES ");
for (int i = 0; i < BULK_COUNT; ++i)
{
sLog.Info(LOG_SERVER, "Query: %s", query.c_str());
Share share = Pop();
query += Util::FS("(INET_NTOA(%u), '%s', %u, 0, '%s', FROM_UNIXTIME(%u), %u)", share.host, share.username.c_str(), share.result, share.reason.c_str(), share.time, share.diff);
if (!Size())
break;
if (i != BULK_COUNT-1)
query += ',';
}
sDatabase.ExecuteAsync(query.c_str());
}
}
DataMgr<Share> sDataMgr;

47
src/server/poolserver/DataMgr/DataMgr.h

@ -0,0 +1,47 @@
#ifndef DATAMGR_H_
#define DATAMGR_H_
#include <deque>
#include <boost/thread.hpp>
#include "Util.h"
#include "Share.h"
#define BULK_MIN 1
#define BULK_COUNT 50
template<class T>
class DataMgr
{
public:
DataMgr() {}
void Push(T data)
{
boost::unique_lock<boost::mutex> lock(_datamutex);
_datastore.push_back(data);
}
T Pop()
{
boost::unique_lock<boost::mutex> lock(_datamutex);
Share share = _datastore.front();
_datastore.pop_front();
return share;
}
size_t Size()
{
boost::unique_lock<boost::mutex> lock(_datamutex);
return _datastore.size();
}
void Upload();
private:
boost::mutex _datamutex;
std::deque<T> _datastore;
};
extern DataMgr<Share> sDataMgr;
#endif

22
src/server/poolserver/DataMgr/Share.h

@ -0,0 +1,22 @@
#ifndef SHARE_H_
#define SHARE_H_
#include <string>
#include "Common.h"
class Share
{
public:
Share(uint32 ahost, std::string ausername, bool aresult, std::string areason, uint64 atime, uint64 adiff):
host(ahost), username(ausername), result(aresult), reason(areason), time(atime), diff(adiff) {}
uint64 host;
std::string username;
bool result;
std::string reason;
uint64 time;
uint64 diff;
};
#endif

15
src/server/poolserver/Main.cpp

@ -26,15 +26,16 @@ bool InitConfig(int argc, char *argv[])
// Generic // Generic
descGeneric.add_options() descGeneric.add_options()
("version,v", "print version string") ("version,v", "Print version string")
("help,h", "produce help message") ("help,h", "Produce help message")
("config,c", boost::program_options::value<std::string>()->default_value("poolserver.cfg"),"name of a file of a configuration.") ("config,c", boost::program_options::value<std::string>()->default_value("../etc/poolserver.cfg"),"Path to configuration file")
; ;
// Server // Server
descServer.add_options() descServer.add_options()
("MinDiffTime", boost::program_options::value<uint32_t>()->default_value(100), "Minimum server diff time") ("MinDiffTime", boost::program_options::value<uint32_t>()->default_value(100), "Minimum server diff time")
("MiningAddress", boost::program_options::value<std::string>()->default_value("n1w8gkPXdNGb6edm4vujBn71A72eQFCNCw"), "Address to send coins to") ("MiningAddress", boost::program_options::value<std::string>()->default_value("n1w8gkPXdNGb6edm4vujBn71A72eQFCNCw"), "Address to send coins to")
("BitcoinRPC", boost::program_options::value<std::vector<std::string> >()->multitoken(), "Bitcoin RPC login credentials")
; ;
// Stratum // Stratum
@ -42,13 +43,14 @@ bool InitConfig(int argc, char *argv[])
("StratumHost,sh", boost::program_options::value<std::string>()->default_value("0.0.0.0"), "Stratum server host") ("StratumHost,sh", boost::program_options::value<std::string>()->default_value("0.0.0.0"), "Stratum server host")
("StratumPort,sp", boost::program_options::value<uint16_t>()->default_value(3333), "Stratum server port") ("StratumPort,sp", boost::program_options::value<uint16_t>()->default_value(3333), "Stratum server port")
("StratumBlockCheckTime", boost::program_options::value<uint32>()->default_value(2000), "Time between block checks in ms") ("StratumBlockCheckTime", boost::program_options::value<uint32>()->default_value(2000), "Time between block checks in ms")
("StratumMinDifficulty", boost::program_options::value<uint32>()->default_value(1), "The difficulty on which a new miner starts")
; ;
// Logging // Logging
descLogging.add_options() descLogging.add_options()
("LogConsoleLevel", boost::program_options::value<uint32_t>()->default_value(LOG_LEVEL_INFO), "Console log level (0-None, 1-Error, 2-Warn, 3-Info, 4-Debug)") ("LogConsoleLevel", boost::program_options::value<uint32_t>()->default_value(LOG_LEVEL_INFO), "Console log level (0-None, 1-Error, 2-Warn, 3-Info, 4-Debug)")
("LogConsoleDebugMask", boost::program_options::value<uint32_t>()->default_value(0), "Console log debug mask") ("LogConsoleDebugMask", boost::program_options::value<uint32_t>()->default_value(0), "Console log debug mask")
("LogFilePath", boost::program_options::value<std::string>()->default_value("."), "File log path") ("LogFilePath", boost::program_options::value<std::string>()->default_value("../etc"), "File log path")
("LogFileLevel", boost::program_options::value<uint32_t>()->default_value(LOG_LEVEL_WARN), "File log level (0-None, 1-Error, 2-Warn, 3-Info, 4-Debug)") ("LogFileLevel", boost::program_options::value<uint32_t>()->default_value(LOG_LEVEL_WARN), "File log level (0-None, 1-Error, 2-Warn, 3-Info, 4-Debug)")
("LogFileDebugMask", boost::program_options::value<uint32_t>()->default_value(0), "File log debug mask") ("LogFileDebugMask", boost::program_options::value<uint32_t>()->default_value(0), "File log debug mask")
; ;
@ -88,6 +90,8 @@ bool InitConfig(int argc, char *argv[])
return true; return true;
} }
sLog.Info(LOG_GENERAL, "Using config file: %s", sConfig.Get<std::string>("config").c_str());
store(parse_config_file(ifs, fileOptions), sConfig.vm); store(parse_config_file(ifs, fileOptions), sConfig.vm);
notify(sConfig.vm); notify(sConfig.vm);
@ -102,7 +106,8 @@ int main(int argc, char *argv[])
sLog.OpenLogFile(sConfig.Get<std::string>("LogFilePath")); sLog.OpenLogFile(sConfig.Get<std::string>("LogFilePath"));
sLog.Info(LOG_GENERAL, "LogFile Started: %s", sLog.logFileLoc.c_str()); sLog.Info(LOG_GENERAL, "LogFile Started: %s", sLog.logFileLoc.c_str());
Server* server = new Server(); boost::asio::io_service io;
Server* server = new Server(io);
int exitcode = server->Run(); int exitcode = server->Run();
delete server; delete server;

188
src/server/poolserver/NetworkMgr/NetworkMgr.cpp

@ -0,0 +1,188 @@
#include "NetworkMgr.h"
#include "Config.h"
NetworkMgr* NetworkMgr::singleton = 0;
NetworkMgr::NetworkMgr(asio::io_service& io_service) : _io_service(io_service), _blockNotifyTimer(io_service), _blockCheckTimer(io_service), _blockHeight(0)
{
BlockCheckTimerStart();
BlockNotifyTimerStart();
}
NetworkMgr::~NetworkMgr()
{
for (int i = 0; i < _cons.size(); ++i)
delete _cons[i];
}
// Bitcoin daemon connection
void NetworkMgr::Connect(JSONRPCConnectionInfo coninfo)
{
JSONRPC* bitcoinrpc = new JSONRPC();
bitcoinrpc->Connect(coninfo);
JSON response = bitcoinrpc->Query("getinfo");
if (response["error"].GetType() != JSON_NULL)
throw Exception(Util::FS("Failed to get response from bitcoin rpc: %s", response["error"].GetString().c_str()));
sLog.Info(LOG_SERVER, "Connected to Bitcoin RPC at %s:%s", coninfo.Host.c_str(), coninfo.Port.c_str());
_cons.push_back(bitcoinrpc);
// Fetch block height on first connection
if (_cons.size() == 1)
BlockCheck();
}
// Get new block template
void NetworkMgr::UpdateBlockTemplate()
{
// Might be called by block notify timer and block check timer so we need a lock
boost::lock_guard<boost::mutex> guard(_mtxBlockTmpl);
for (int i = 0; i < _cons.size(); ++i)
{
try {
JSON response = _cons[i]->Query("getblocktemplate");
// New blocks may not appear on all daemons the same time
if (response["height"].GetInt() < _blockHeight)
continue;
Bitcoin::BlockPtr block = Bitcoin::BlockPtr(new Bitcoin::Block());
block->version = response["version"].GetInt();
block->prevBlockHash = Util::Reverse(Util::ASCIIToBin(response["previousblockhash"].GetString()));
block->time = response["curtime"].GetInt();
// Set bits
ByteBuffer bitbuf(Util::Reverse(Util::ASCIIToBin(response["bits"].GetString())));
bitbuf >> block->bits;
// Add coinbase tx
BinaryData pubkey = Util::ASCIIToBin(sConfig.Get<std::string>("MiningAddress"));
block->tx.push_back(Bitcoin::CreateCoinbaseTX(_blockHeight, pubkey, response["coinbasevalue"].GetInt()));
// Add other transactions
JSON trans = response["transactions"];
for (uint64 tidx = 0; tidx < trans.Size(); ++tidx) {
ByteBuffer txbuf(Util::ASCIIToBin(trans[tidx]["data"].GetString()));
Bitcoin::Transaction tx;
txbuf >> tx;
block->tx.push_back(tx);
}
// Genrate merkle tree
block->BuildMerkleTree();
// Set
_curBlockTmpl = block;
sLog.Debug(LOG_SERVER, "Fetched block template from rpc #%u", i);
// Break from loop
break;
} catch (std::exception& e) {
sLog.Error(LOG_SERVER, "Failed to fetch block template from daemon #%u: %s", i, e.what());
}
}
}
// Submit new block
bool NetworkMgr::SubmitBlock(Bitcoin::Block block)
{
// Serialize block
ByteBuffer blockbuf;
blockbuf << block;
for (int i = 0; i < _cons.size(); ++i)
{
try {
JSON params;
params.Add(Util::BinToASCII(blockbuf.Binary()));
JSON response = _cons[i]->Query("submitblock", params);
if (response["result"].GetType() == JSON_NULL) {
sLog.Info(LOG_SERVER, "Block accepted! YAY!");
BlockCheck();
return true;
} else {
sLog.Error(LOG_SERVER, "Block rejected: %s", response["error"].GetString().c_str());
}
} catch (std::exception& e) {
sLog.Error(LOG_SERVER, "Exception caught while submiting block: %s", e.what());
}
}
return false;
}
// Check for new blocks
void NetworkMgr::BlockCheck()
{
// Might be called twice from timer and when block is found
boost::lock_guard<boost::mutex> guard(_mtxBlockCheck);
for (int i = 0; i < _cons.size(); ++i)
{
try {
JSON response = _cons[i]->Query("getinfo");
uint32 curBlock = response["blocks"].GetInt();
if (curBlock > _blockHeight) {
sLog.Debug(LOG_SERVER, "New block on network! Height: %u", curBlock);
_blockHeight = curBlock;
// Update block template
UpdateBlockTemplate();
// Send notifications (and reset work)
BlockNotifySend(true);
}
} catch (std::exception& e) {
sLog.Error(LOG_SERVER, "Failed to fetch block height from daemon #%u: %s", i, e.what());
}
}
}
void NetworkMgr::BlockCheckTimerStart()
{
_blockCheckTimer.expires_from_now(boost::posix_time::seconds(3));
_blockCheckTimer.async_wait(boost::bind(&NetworkMgr::BlockCheckTimerExpired, this, boost::asio::placeholders::error));
}
void NetworkMgr::BlockCheckTimerExpired(const boost::system::error_code& /*e*/)
{
BlockCheck();
BlockCheckTimerStart();
}
// Bind for receiving block notifications
void NetworkMgr::BlockNotifyBind(FBlockNotify f)
{
_blockNotifyBinds.push_back(f);
}
// Block notify timer
void NetworkMgr::BlockNotifyTimerStart()
{
_blockNotifyTimer.expires_from_now(boost::posix_time::seconds(3));
_blockNotifyTimer.async_wait(boost::bind(&NetworkMgr::BlockNotifyTimerExpired, this, boost::asio::placeholders::error));
}
void NetworkMgr::BlockNotifyTimerExpired(const boost::system::error_code& /*e*/)
{
sLog.Debug(LOG_SERVER, "Sending block template update");
UpdateBlockTemplate();
BlockNotifySend();
BlockNotifyTimerStart();
}
// Send block notification to all subscribers
void NetworkMgr::BlockNotifySend(bool newBlock)
{
for (int i = 0; i < _blockNotifyBinds.size(); ++i)
_io_service.post(boost::bind(_blockNotifyBinds[i], _curBlockTmpl, newBlock));
}

84
src/server/poolserver/NetworkMgr/NetworkMgr.h

@ -0,0 +1,84 @@
#ifndef NETWORKMGR_H_
#define NETWORKMGR_H_
// Provides high level interaction with bitcoin daemon
#include "JSONRPC.h"
#include "Bitcoin.h"
#include "Log.h"
#include <vector>
#include <boost/asio.hpp>
#include <boost/function.hpp>
#include <boost/thread/mutex.hpp>
using namespace boost;
typedef boost::function<void (Bitcoin::BlockPtr /*blockTmpl*/, bool /*newBlock*/)> FBlockNotify;
class NetworkMgr
{
// Singleton
private:
static NetworkMgr* singleton;
public:
static NetworkMgr* Instance()
{
return singleton;
}
static void Initialize(asio::io_service& io_service)
{
singleton = new NetworkMgr(io_service);
}
public:
NetworkMgr(asio::io_service& io_service);
~NetworkMgr();
// Bitcoin daemon connection
void Connect(JSONRPCConnectionInfo coninfo);
// Get new block template
void UpdateBlockTemplate();
// Submit new block
bool SubmitBlock(Bitcoin::Block block);
// Checking for blocks
void BlockCheck();
void BlockCheckTimerStart();
void BlockCheckTimerExpired(const boost::system::error_code& /*e*/);
// Bind for receiving block notifications
void BlockNotifyBind(FBlockNotify f);
// Block notify timer
void BlockNotifyTimerStart();
void BlockNotifyTimerExpired(const boost::system::error_code& /*e*/);
// Send block notification to all subscribers
void BlockNotifySend(bool newBlock = false);
private:
// Holds subscriptions for block notifications
std::vector<FBlockNotify> _blockNotifyBinds;
boost::asio::deadline_timer _blockNotifyTimer;
// Checking for new blocks
boost::asio::deadline_timer _blockCheckTimer;
boost::mutex _mtxBlockCheck;
uint32 _blockHeight;
// Connections to bitcoin rpc
std::vector<JSONRPC*> _cons;
// Current block template
Bitcoin::BlockPtr _curBlockTmpl;
boost::mutex _mtxBlockTmpl;
// ASIO
asio::io_service& _io_service;
};
#endif

76
src/server/poolserver/Server/Server.cpp

@ -6,22 +6,16 @@
#include "ServerDatabaseEnv.h" #include "ServerDatabaseEnv.h"
#include "Crypto.h" #include "Crypto.h"
#include "Bitcoin.h" #include "Bitcoin.h"
#include "DataMgr.h"
#include "NetworkMgr.h"
#include "Exception.h"
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
struct Share
{ Server::Server(asio::io_service& io) : serverLoops(0), io_service(io), uploadtimer(io)
Share(uint64 _id, uint32 _diff, uint32 _workerid, uint32 _timestamp) :
id(_id), diff(_diff), workerid(_workerid), timestamp(_timestamp) {}
uint64 id;
uint32 diff;
uint32 workerid;
uint32 timestamp;
};
Server::Server() : serverLoops(0)
{ {
} }
@ -34,13 +28,22 @@ void AsyncQueryCallback(MySQL::QueryResult result)
{ {
} }
void Server::UploadShares(const boost::system::error_code& /*e*/)
{
sDataMgr.Upload();
uploadtimer.expires_from_now(boost::posix_time::seconds(3)); //repeat rate here
uploadtimer.async_wait(boost::bind(&Server::UploadShares, this, boost::asio::placeholders::error));
}
int Server::Run() int Server::Run()
{ {
sLog.Info(LOG_SERVER, "Server is starting..."); sLog.Info(LOG_SERVER, "Server is starting...");
InitDatabase(); InitDatabase();
std::vector<Share> shares; /*std::vector<Share> shares;
sLog.Info(LOG_SERVER, "Loading shares..."); sLog.Info(LOG_SERVER, "Loading shares...");
@ -51,7 +54,7 @@ int Server::Run()
sLog.Info(LOG_SERVER, "Min: %u Max: %u", min, max); sLog.Info(LOG_SERVER, "Min: %u Max: %u", min, max);
MySQL::PreparedStatement* stmt = sDatabase.GetPreparedStatement(STMT_QUERY_SHARES); MySQL::PreparedStatement* stmt = sDatabase.GetPreparedStatement(STMT_QUERY_SHARES);
for (uint32 i = min; i < max; i += 500000) for (uint32 i = min; i <= max; i += 500000)
{ {
stmt->SetUInt32(0, i); stmt->SetUInt32(0, i);
MySQL::QueryResult result2 = sDatabase.Query(stmt); MySQL::QueryResult result2 = sDatabase.Query(stmt);
@ -61,11 +64,7 @@ int Server::Run()
sLog.Info(LOG_SERVER, "Shares: %u", shares.size()); sLog.Info(LOG_SERVER, "Shares: %u", shares.size());
} }
sLog.Info(LOG_SERVER, "Loaded %u shares", shares.size()); sLog.Info(LOG_SERVER, "Loaded %u shares", shares.size());*/
JSON node = JSON::FromString("{\"test\":{\"omg\":\"smth\"},\"other\":[\"smth2\", \"smth3\"] }");
sLog.Info(LOG_SERVER, "Something2: %s", node["other"][0].GetString().c_str());
sLog.Info(LOG_SERVER, "Something: %s", node.ToString().c_str());
/*std::vector<byte> test = Util::ASCIIToBin("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); /*std::vector<byte> test = Util::ASCIIToBin("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
sLog.Info(LOG_SERVER, "Hash: %s", Util::BinToASCII(Crypto::SHA256D(test)).c_str()); sLog.Info(LOG_SERVER, "Hash: %s", Util::BinToASCII(Crypto::SHA256D(test)).c_str());
@ -103,31 +102,34 @@ int Server::Run()
// Main io service NetworkMgr::Initialize(io_service);
asio::io_service io_service;
Stratum::Server srv(io_service); std::vector<std::string> btcrpc = sConfig.Get<std::vector<std::string> >("BitcoinRPC");
for (int i = 0; i < btcrpc.size(); ++i) {
std::vector<std::string> params = Util::Explode(btcrpc[i], ";");
if (params.size() != 4)
throw Exception("Invalid Bitcoin RPC parameters");
JSONRPCConnectionInfo coninfo;
coninfo.Host = params[0];
coninfo.Port = params[1];
coninfo.User = params[2];
coninfo.Pass = params[3];
NetworkMgr::Instance()->Connect(coninfo);
}
// Init Bitcoin RPC Stratum::Server srv(io_service);
/*JSONRPCConnectionInfo coninfo;
coninfo.Host = "84.240.15.208";
coninfo.Port = "8332";
coninfo.User = "user";
coninfo.Pass = "DYAL6bC4RUHksL6ikdx7";*/
JSONRPCConnectionInfo coninfo;
coninfo.Host = "127.0.0.1";
coninfo.Port = "19001";
coninfo.User = "test";
coninfo.Pass = "123";
JSONRPC* bitcoinrpc = new JSONRPC();
bitcoinrpc->Connect(coninfo);
srv.SetBitcoinRPC(bitcoinrpc);
// Start stratum server // Start stratum server
tcp::endpoint endpoint(tcp::v4(), sConfig.Get<uint16>("StratumPort")); tcp::endpoint endpoint(tcp::v4(), sConfig.Get<uint16>("StratumPort"));
srv.Start(endpoint); srv.Start(endpoint);
// Start timer
uploadtimer.expires_from_now(boost::posix_time::seconds(3)); //repeat rate here
uploadtimer.async_wait(boost::bind(&Server::UploadShares, this, boost::asio::placeholders::error));
io_service.run(); io_service.run();
@ -182,7 +184,7 @@ int Server::Run()
// Mercy for CPU // Mercy for CPU
if (diff < minDiffTime+sleepDuration) { if (diff < minDiffTime+sleepDuration) {
sleepDuration = minDiffTime - diff + sleepDuration; sleepDuration = minDiffTime - diff + sleepDuration;
boost::this_thread::sleep_for(boost::chrono::milliseconds(sleepDuration)); boost::this_thread::sleep(boost::posix_time::milliseconds(sleepDuration));
} else } else
sleepDuration = 0; sleepDuration = 0;

6
src/server/poolserver/Server/Server.h

@ -12,11 +12,12 @@
class Server class Server
{ {
public: public:
Server(); Server(asio::io_service& io);
~Server(); ~Server();
Stratum::Server* stratumServer; Stratum::Server* stratumServer;
void UploadShares(const boost::system::error_code& e);
boost::chrono::steady_clock::time_point diffStart; boost::chrono::steady_clock::time_point diffStart;
bool running; bool running;
uint64_t serverLoops; uint64_t serverLoops;
@ -25,6 +26,9 @@ public:
void Update(uint32_t); void Update(uint32_t);
bool InitDatabase(); bool InitDatabase();
boost::asio::deadline_timer uploadtimer;
asio::io_service& io_service;
}; };
#endif #endif

78
src/server/poolserver/Stratum/Client.cpp

@ -1,6 +1,9 @@
#include "Server.h" #include "Server.h"
#include "Client.h" #include "Client.h"
#include "BigNum.h" #include "BigNum.h"
#include "DataMgr.h"
#include "ShareLimiter.h"
#include "Exception.h"
#include <iostream> #include <iostream>
namespace Stratum namespace Stratum
@ -55,11 +58,23 @@ namespace Stratum
{ {
JSON params = msg["params"]; JSON params = msg["params"];
std::string username = params[0].GetString(); // Share limiter
if (!_shareLimiter.Submit()) {
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(20));
response["error"].Add("Blocked by share limiter");
response["error"].Add(JSON());
SendMessage(response);
return;
}
uint32 jobid; uint32 jobid;
ByteBuffer jobbuf(Util::ASCIIToBin(params[1].GetString())); ByteBuffer jobbuf(Util::ASCIIToBin(params[1].GetString()));
jobbuf >> jobid; jobbuf >> jobid;
// Check if such job exists
if (!_jobs.count(jobid)) { if (!_jobs.count(jobid)) {
JSON response; JSON response;
response["id"] = msg["id"]; response["id"] = msg["id"];
@ -72,25 +87,47 @@ namespace Stratum
} }
// check username // check username
std::string username = params[0].GetString();
BinaryData extranonce2 = Util::ASCIIToBin(params[2].GetString()); BinaryData extranonce2 = Util::ASCIIToBin(params[2].GetString());
if (extranonce2.size() != 4) { if (extranonce2.size() != 4) {
sLog.Error(LOG_SERVER, "Wrong extranonce size"); sLog.Error(LOG_SERVER, "Wrong extranonce size");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(20));
response["error"].Add("Wrong extranonce size");
response["error"].Add(JSON());
SendMessage(response);
return; return;
} }
ByteBuffer timebuf(Util::Reverse(Util::ASCIIToBin(params[3].GetString()))); ByteBuffer timebuf(Util::Reverse(Util::ASCIIToBin(params[3].GetString())));
if (timebuf.Size() != 4) { if (timebuf.Size() != 4) {
sLog.Error(LOG_SERVER, "Wrong ntime size"); sLog.Error(LOG_SERVER, "Wrong ntime size");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(20));
response["error"].Add("Wrong ntime size");
response["error"].Add(JSON());
SendMessage(response);
return; return;
} }
ByteBuffer noncebuf(Util::Reverse(Util::ASCIIToBin(params[4].GetString()))); ByteBuffer noncebuf(Util::Reverse(Util::ASCIIToBin(params[4].GetString())));
if (noncebuf.Size() != 4) { if (noncebuf.Size() != 4) {
sLog.Error(LOG_SERVER, "Wrong nonce size"); sLog.Error(LOG_SERVER, "Wrong nonce size");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(20));
response["error"].Add("Wrong nonce size");
response["error"].Add(JSON());
SendMessage(response);
return; return;
} }
// Get job // Get job
Job& job = _jobs[jobid]; Job& job = _jobs[jobid];
@ -98,6 +135,13 @@ namespace Stratum
share << extranonce2 << timebuf << noncebuf; share << extranonce2 << timebuf << noncebuf;
if (!job.SubmitShare(share.Binary())) { if (!job.SubmitShare(share.Binary())) {
sLog.Error(LOG_SERVER, "Duplicate share"); sLog.Error(LOG_SERVER, "Duplicate share");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(22));
response["error"].Add("Duplicate share");
response["error"].Add(JSON());
SendMessage(response);
return; return;
} }
@ -140,12 +184,34 @@ namespace Stratum
std::cout << "Criteria: " << criteria << std::endl; std::cout << "Criteria: " << criteria << std::endl;
std::cout << "Diff: " << diff << std::endl; std::cout << "Diff: " << diff << std::endl;
// Check if difficulty meets job diff
if (target > Bitcoin::DiffToTarget(job.diff)) {
sLog.Error(LOG_SERVER, "Share above target");
JSON response;
response["id"] = msg["id"];
response["result"];
response["error"].Add(int64(23));
response["error"].Add("Share above target");
response["error"].Add(JSON());
SendMessage(response);
return;
}
// Check if block meets criteria // Check if block meets criteria
if (target <= criteria) { if (target <= criteria) {
sLog.Info(LOG_SERVER, "We have found a block candidate!"); sLog.Info(LOG_SERVER, "We have found a block candidate!");
_server->SubmitBlock(block); _server->SubmitBlock(block);
} } else {
sDataMgr.Push(Share(_ip, username, true, "", Util::Date(), job.diff));
JSON response;
response["id"] = msg["id"];
response["result"] = true;
response["error"];
SendMessage(response);
return;
}
} }
void Client::OnMiningSubscribe(JSON msg) void Client::OnMiningSubscribe(JSON msg)
@ -192,6 +258,7 @@ namespace Stratum
{ {
Job job; Job job;
job.block = _server->GetWork(); job.block = _server->GetWork();
job.diff = 1;
// Serialize transaction // Serialize transaction
ByteBuffer coinbasebuf; ByteBuffer coinbasebuf;
@ -219,6 +286,11 @@ namespace Stratum
iss << is.rdbuf(); iss << is.rdbuf();
sLog.Debug(LOG_SERVER, "Received: %s", iss.str().c_str()); sLog.Debug(LOG_SERVER, "Received: %s", iss.str().c_str());
OnMessage(JSON::FromString(iss.str())); OnMessage(JSON::FromString(iss.str()));
/*try {
OnMessage(JSON::FromString(iss.str()));
} catch (uint64 e) {
sLog.Error(LOG_SERVER, "Exception caught while parsing json: %s", e.what());
}*/
StartRead(); StartRead();
} else { } else {

48
src/server/poolserver/Stratum/Client.h

@ -2,10 +2,12 @@
#define STRATUM_CLIENT_H_ #define STRATUM_CLIENT_H_
#include "Common.h" #include "Common.h"
#include "Config.h"
#include "Log.h" #include "Log.h"
#include "JSON.h" #include "JSON.h"
#include "Server.h" #include "Server.h"
#include "Job.h" #include "Job.h"
#include "ShareLimiter.h"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
@ -24,8 +26,10 @@ namespace Stratum
class Client : public boost::enable_shared_from_this<Client> class Client : public boost::enable_shared_from_this<Client>
{ {
public: public:
Client(Server* server, asio::io_service& io_service) : _server(server), _socket(io_service), _subscribed(false), _jobid(0) Client(Server* server, asio::io_service& io_service, uint64 id) : _server(server), _socket(io_service), _id(id), _subscribed(false), _jobid(0), _shareLimiter(this)
{ {
_diff = sConfig.Get<uint32>("StratumMinDifficulty");
_minDiff = _diff;
} }
tcp::socket& GetSocket() tcp::socket& GetSocket()
@ -35,6 +39,11 @@ namespace Stratum
void Start() void Start()
{ {
// Get IP
tcp::endpoint remote_ep = _socket.remote_endpoint();
address remote_ad = remote_ep.address();
_ip = remote_ad.to_v4().to_ulong();
// Start reading socket // Start reading socket
StartRead(); StartRead();
} }
@ -84,6 +93,36 @@ namespace Stratum
// Gets new job from the server // Gets new job from the server
Job GetJob(); Job GetJob();
// Worker difficulty
uint64 GetDifficulty()
{
return _diff;
}
void SetDifficulty(uint64 diff, bool resendJob = false)
{
_diff = diff;
// Send difficulty update
JSON params;
params.Add(int64(_diff));
JSON msg;
msg["id"];
msg["params"] = params;
msg["method"] = "mining.set_difficulty";
SendMessage(msg);
if (resendJob)
SendJob(false);
}
// Client ID
uint64 GetID()
{
return _id;
}
void Disconnect() void Disconnect()
{ {
_socket.close(); _socket.close();
@ -96,6 +135,8 @@ namespace Stratum
// Networking // Networking
asio::streambuf _recvBuffer; asio::streambuf _recvBuffer;
tcp::socket _socket; tcp::socket _socket;
uint32 _ip;
uint64 _id;
// Pointer to server // Pointer to server
Stratum::Server* _server; Stratum::Server* _server;
@ -108,6 +149,11 @@ namespace Stratum
uint32 _extranonce; uint32 _extranonce;
std::map<uint64, Job> _jobs; std::map<uint64, Job> _jobs;
uint32 _jobid; uint32 _jobid;
// Share limiting
uint64 _diff;
uint64 _minDiff;
ShareLimiter _shareLimiter;
}; };
typedef boost::shared_ptr<Client> ClientPtr; typedef boost::shared_ptr<Client> ClientPtr;

1
src/server/poolserver/Stratum/Job.h

@ -11,6 +11,7 @@ namespace Stratum
class Job class Job
{ {
public: public:
uint64 diff;
Bitcoin::BlockPtr block; Bitcoin::BlockPtr block;
BinaryData coinbase1; BinaryData coinbase1;
BinaryData coinbase2; BinaryData coinbase2;

27
src/server/poolserver/Stratum/Server.cpp

@ -4,9 +4,6 @@ namespace Stratum
{ {
void Server::Start(tcp::endpoint endpoint) void Server::Start(tcp::endpoint endpoint)
{ {
// Start block checking timer
_CheckBlocksTimer();
_acceptor.open(endpoint.protocol()); _acceptor.open(endpoint.protocol());
_acceptor.set_option(tcp::acceptor::reuse_address(true)); _acceptor.set_option(tcp::acceptor::reuse_address(true));
_acceptor.bind(endpoint); _acceptor.bind(endpoint);
@ -24,33 +21,15 @@ namespace Stratum
_io_service.post(boost::bind(&Client::SendMessage, (*it), msg)); _io_service.post(boost::bind(&Client::SendMessage, (*it), msg));
} }
void Server::ResetWork() void Server::SendBlockTmpl(bool resetWork)
{ {
std::set<ClientPtr>::iterator it; std::set<ClientPtr>::iterator it;
for (it = _clients.begin(); it != _clients.end(); ++it) for (it = _clients.begin(); it != _clients.end(); ++it)
_io_service.post(boost::bind(&Client::SendJob, (*it), true)); _io_service.post(boost::bind(&Client::SendJob, (*it), resetWork));
} }
bool Server::SubmitBlock(Bitcoin::Block block) bool Server::SubmitBlock(Bitcoin::Block block)
{ {
// Serialize block return NetworkMgr::Instance()->SubmitBlock(block);
ByteBuffer blockbuf;
blockbuf << block;
try {
JSON params;
params.Add(Util::BinToASCII(blockbuf.Binary()));
JSON response = _bitcoinrpc->Query("submitblock", params);
if (response["result"].GetType() == JSON_NULL) {
sLog.Info(LOG_STRATUM, "Block accepted! YAY!");
_io_service.post(boost::bind(&Server::_CheckBlocks, this));
return true;
} else {
sLog.Info(LOG_STRATUM, "Block rejected! Booooo");
}
} catch (...) {
return false;
}
} }
} }

146
src/server/poolserver/Stratum/Server.h

@ -10,6 +10,7 @@
#include "Bitcoin.h" #include "Bitcoin.h"
#include "Util.h" #include "Util.h"
#include "ByteBuffer.h" #include "ByteBuffer.h"
#include "NetworkMgr.h"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
@ -27,12 +28,22 @@ namespace Stratum
uint16 Port; uint16 Port;
}; };
// Used for sorting std::set
struct ClientPtrCMP
{
bool operator() (const ClientPtr& a, const ClientPtr& b)
{
return a->GetID() < b->GetID();
}
};
class Server class Server
{ {
public: public:
Server(asio::io_service& io_service) : _io_service(io_service), _acceptor(io_service), _blockCheckTimer(io_service), _blockHeight(0), _extranonce(0) Server(asio::io_service& io_service) : _io_service(io_service), _acceptor(io_service), _extranonce(0), _clientId(0)
{ {
_pubkey = Util::ASCIIToBin(sConfig.Get<std::string>("MiningAddress")); // Subscribe for block updates
NetworkMgr::Instance()->BlockNotifyBind(boost::bind(&Server::BlockNotify, this, _1, _2));
} }
~Server() ~Server()
@ -45,11 +56,6 @@ namespace Stratum
// Starts accepting connections // Starts accepting connections
void Start(tcp::endpoint endpoint); void Start(tcp::endpoint endpoint);
void SetBitcoinRPC(JSONRPC* bitcoinrpc)
{
_bitcoinrpc = bitcoinrpc;
}
// Sends message to all clients // Sends message to all clients
void SendToAll(JSON msg); void SendToAll(JSON msg);
@ -62,12 +68,22 @@ namespace Stratum
// Returns current work // Returns current work
Bitcoin::BlockPtr GetWork() Bitcoin::BlockPtr GetWork()
{ {
boost::lock_guard<boost::mutex> guard(_mtx_workupdate); boost::lock_guard<boost::mutex> guard(_mtxCurrentWork);
return _currentWork; return _currentWork;
} }
// Block template update event
void BlockNotify(Bitcoin::BlockPtr block, bool newBlock)
{
sLog.Debug(LOG_SERVER, "Received block template update");
_mtxCurrentWork.lock();
_currentWork = block;
_mtxCurrentWork.unlock();
SendBlockTmpl(newBlock);
}
// Resets work for all clients // Resets work for all clients
void ResetWork(); void SendBlockTmpl(bool resetWork);
// Submits block to bitcoind // Submits block to bitcoind
bool SubmitBlock(Bitcoin::Block block); bool SubmitBlock(Bitcoin::Block block);
@ -82,7 +98,7 @@ namespace Stratum
private: private:
void _StartAccept() void _StartAccept()
{ {
ClientPtr client = ClientPtr(new Client(this, _io_service)); ClientPtr client = ClientPtr(new Client(this, _io_service, _clientId++));
_acceptor.async_accept(client->GetSocket(), boost::bind(&Server::_OnAccept, this, client, asio::placeholders::error)); _acceptor.async_accept(client->GetSocket(), boost::bind(&Server::_OnAccept, this, client, asio::placeholders::error));
} }
@ -90,7 +106,7 @@ namespace Stratum
void _OnAccept(ClientPtr client, const boost::system::error_code& error) void _OnAccept(ClientPtr client, const boost::system::error_code& error)
{ {
if (!error) { if (!error) {
sLog.Debug(LOG_STRATUM, "New stratum client accepted"); sLog.Debug(LOG_STRATUM, "New stratum client accepted. Total clients: %u", _clients.size());
client->Start(); client->Start();
_clients.insert(client); _clients.insert(client);
} else { } else {
@ -99,117 +115,19 @@ namespace Stratum
_StartAccept(); _StartAccept();
} }
void _UpdateWork(bool reset)
{
JSON response = _bitcoinrpc->Query("getblocktemplate");
Bitcoin::BlockPtr block = Bitcoin::BlockPtr(new Bitcoin::Block());
block->version = response["version"].GetInt();
block->prevBlockHash = Util::Reverse(Util::ASCIIToBin(response["previousblockhash"].GetString()));
block->time = response["curtime"].GetInt();
// Set bits
ByteBuffer bitbuf(Util::Reverse(Util::ASCIIToBin(response["bits"].GetString())));
bitbuf >> block->bits;
// Add coinbase tx
block->tx.push_back(CreateCoinbaseTX(_blockHeight, _pubkey, response["coinbasevalue"].GetInt()));
// Add other transactions
JSON trans = response["transactions"];
for (uint64 i = 0; i < trans.Size(); ++i) {
ByteBuffer txbuf(Util::ASCIIToBin(trans[i]["data"].GetString()));
Bitcoin::Transaction tx;
txbuf >> tx;
block->tx.push_back(tx);
}
// Genrate merkle tree
block->BuildMerkleTree();
// Set current work
_mtx_workupdate.lock();
_currentWork = block;
_mtx_workupdate.unlock();
// Requests all clients to reset work
if (reset)
ResetWork();
}
void _CheckBlocksTimer()
{
_CheckBlocks();
_blockCheckTimer.expires_from_now(boost::posix_time::milliseconds(sConfig.Get<uint32>("StratumBlockCheckTime")));
_blockCheckTimer.async_wait(boost::bind(&Server::_CheckBlocksTimer, this));
}
void _CheckBlocks()
{
// Might be called twice from timer and when block is found
boost::lock_guard<boost::mutex> guard(_mtx_checkblock);
sLog.Debug(LOG_STRATUM, "Clients: %u", _clients.size());
JSON response = _bitcoinrpc->Query("getinfo");
uint32 curBlock = response["blocks"].GetInt();
if (curBlock > _blockHeight) {
sLog.Debug(LOG_STRATUM, "New block on network! Height: %u", curBlock);
_blockHeight = curBlock;
_UpdateWork(true);
}
}
Bitcoin::Transaction CreateCoinbaseTX(uint32 blockHeight, BinaryData pubkey, int64 value)
{
// Extranonce placeholder
BinaryData extranonce_ph(8, 0);
ByteBuffer scriptsig;
scriptsig << _blockHeight << extranonce_ph;
Bitcoin::OutPoint outpoint;
outpoint.hash.resize(32, 0);
outpoint.n = 0xFFFFFFFF;
Bitcoin::TxIn txin;
txin.prevout = outpoint;
txin.script = scriptsig.Binary();
txin.n = 0;
Bitcoin::TxOut txout;
txout.value = value;
txout.scriptPubKey = Bitcoin::Script(pubkey) + Bitcoin::OP_CHECKSIG;
Bitcoin::Transaction tx;
tx.version = 1;
tx.in.push_back(txin);
tx.out.push_back(txout);
tx.lockTime = 0;
return tx;
}
private: private:
// Network // Network
std::set<ClientPtr> _clients;
asio::io_service& _io_service; asio::io_service& _io_service;
tcp::acceptor _acceptor; tcp::acceptor _acceptor;
// Mutexes // Clients
boost::mutex _mtx_checkblock; std::set<ClientPtr, ClientPtrCMP> _clients;
boost::mutex _mtx_workupdate; uint64 _clientId;
// RPC
JSONRPC* _bitcoinrpc;
// Bitcoin info
BinaryData _pubkey;
asio::deadline_timer _blockCheckTimer;
uint32 _blockHeight;
// Work // Work
Bitcoin::BlockPtr _currentWork; Bitcoin::BlockPtr _currentWork;
boost::mutex _mtxCurrentWork;
uint32 _extranonce; uint32 _extranonce;
}; };
} }

39
src/server/poolserver/Stratum/ShareLimiter.cpp

@ -0,0 +1,39 @@
#include "ShareLimiter.h"
#include "Server.h"
#include "Client.h"
namespace Stratum
{
// Returning false will stop any further share verifications (DoS prevention, etc)
bool ShareLimiter::Submit()
{
uint64 curTime = Util::Date();
uint64 sinceLast = curTime - _lastRetarget;
_shares.push_back(curTime);
if (sinceLast < RETARGET_INTERVAL)
return true;
while (_shares.size() && (_shares.front() < curTime - RETARGET_TIME_BUFFER))
_shares.pop_front();
uint32 interval = std::min(curTime - _startTime, uint64(RETARGET_TIME_BUFFER));
// Calculate shares/min
double speed = (_shares.size()*60) / interval;
// Calculate difference from pool target in %
double variance = (speed - RETARGET_SHARES_PER_MIN) / RETARGET_SHARES_PER_MIN;
// Check if we need to retarget
if (std::abs(variance)*100 < RETARGET_VARIANCE)
return true;
uint64 newDiff = double(_client->GetDifficulty()) * variance;
_client->SetDifficulty(newDiff, true);
return true;
}
}

44
src/server/poolserver/Stratum/ShareLimiter.h

@ -0,0 +1,44 @@
#ifndef SHARELIMITER_H_
#define SHARELIMITER_H_
#include "Common.h"
#include "Util.h"
#include <deque>
#define RETARGET_INTERVAL 60
#define RETARGET_TIME_BUFFER 60*5
#define RETARGET_SHARES_PER_MIN 15
#define RETARGET_VARIANCE 40
namespace Stratum
{
class Client;
class ShareLimiterRecord
{
public:
ShareLimiterRecord(uint64 atime, uint64 adiff) : time(atime), diff(adiff) {}
uint64 time;
uint64 diff;
};
class ShareLimiter
{
public:
ShareLimiter(Client* client) : _client(client), _lastRetarget(0)
{
_startTime = Util::Date();
}
bool Submit();
private:
std::deque<uint64> _shares;
Client* _client;
uint64 _lastRetarget;
uint64 _startTime;
};
}
#endif

6
src/server/poolserver/poolserver.cfg.dist

@ -72,9 +72,9 @@ StratumPort=3333
# Description: Location to save logfile to # Description: Location to save logfile to
# Important: Do not add trailing slash # Important: Do not add trailing slash
# Example: "/var/log" # Example: "/var/log"
# Default: "." - (Save log file next to executable) # Default: "../etc" - (Save log file next to executable)
LogFilePath="." LogFilePath="../etc"
# #
# LogConsoleLevel # LogConsoleLevel
@ -144,7 +144,7 @@ MySQLPort=3306
# Description: MySQL Server Username # Description: MySQL Server Username
# Default: "" - (No Username) # Default: "" - (No Username)
MySQLUser="" MySQLUser="root"
# #
# MySQLPass # MySQLPass

29
src/server/shared/Bitcoin/Bitcoin.h

@ -30,6 +30,35 @@ namespace Bitcoin
mpz_ui_pow_ui(power, 2, 8 * (nbytes - 3)); mpz_ui_pow_ui(power, 2, 8 * (nbytes - 3));
return BigInt(bits & 0xFFFFFF) * BigInt(power); return BigInt(bits & 0xFFFFFF) * BigInt(power);
} }
inline Transaction CreateCoinbaseTX(uint32 blockHeight, BinaryData pubkey, int64 value)
{
// Extranonce placeholder
BinaryData extranonce_ph(8, 0);
ByteBuffer scriptsig;
scriptsig << blockHeight << extranonce_ph;
Bitcoin::OutPoint outpoint;
outpoint.hash.resize(32, 0);
outpoint.n = 0xFFFFFFFF;
TxIn txin;
txin.prevout = outpoint;
txin.script = scriptsig.Binary();
txin.n = 0;
TxOut txout;
txout.value = value;
txout.scriptPubKey = Bitcoin::Script(pubkey) + Bitcoin::OP_CHECKSIG;
Transaction tx;
tx.version = 1;
tx.in.push_back(txin);
tx.out.push_back(txout);
tx.lockTime = 0;
return tx;
}
} }
#endif #endif

2
src/server/shared/ByteBuffer.h

@ -69,7 +69,7 @@ public:
size_t size = sizeof(T); size_t size = sizeof(T);
if (vec.size() < pointer+size) if (vec.size() < pointer+size)
return NULL; return 0;
T data = 0; T data = 0;
for (uint64 i = 0; i < size; ++i) for (uint64 i = 0; i < size; ++i)

5
src/server/shared/Common.h

@ -3,6 +3,11 @@
#include <boost/cstdint.hpp> #include <boost/cstdint.hpp>
#include <vector> #include <vector>
#include <string>
#include <cstdio>
#include <cstdarg>
#define MAX_FORMAT_LEN 32*1024
typedef uint8_t uint8; typedef uint8_t uint8;
typedef uint16_t uint16; typedef uint16_t uint16;

27
src/server/shared/Exception.h

@ -0,0 +1,27 @@
#ifndef EXCEPTION_H_
#define EXCEPTION_H_
class Exception: public std::exception
{
public:
Exception(const char *text)
{
_msg.assign(text);
}
Exception(std::string text)
{
_msg.assign(text);
}
virtual ~Exception() throw () {}
virtual const char* what() const throw () {
return _msg.c_str();
}
protected:
std::string _msg;
};
#endif

34
src/server/shared/JSON/JSON.h

@ -2,6 +2,9 @@
#define JSON_H_ #define JSON_H_
#include "Common.h" #include "Common.h"
#include "Log.h"
#include "Util.h"
#include "Exception.h"
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/variant.hpp> #include <boost/variant.hpp>
@ -10,7 +13,6 @@
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include <map> #include <map>
#include <Log.h>
enum JSONValueType enum JSONValueType
{ {
@ -25,6 +27,13 @@ enum JSONValueType
typedef boost::variant<bool, int64, double, std::string> JSONValue; typedef boost::variant<bool, int64, double, std::string> JSONValue;
class JSONException: public Exception
{
public:
JSONException(const char *text): Exception(text) {}
JSONException(std::string text): Exception(text) {}
};
class JSON class JSON
{ {
public: public:
@ -41,6 +50,9 @@ public:
// Arrays // Arrays
JSON& operator[] (uint32 index) JSON& operator[] (uint32 index)
{ {
if (index >= _vec.size())
throw JSONException(Util::FS("Index %u out of range. Vector size: %u", index, _vec.size()));
return _vec[index]; return _vec[index];
} }
@ -49,7 +61,7 @@ public:
{ {
if (_type != JSON_ARRAY) { if (_type != JSON_ARRAY) {
if (_type != JSON_NULL) if (_type != JSON_NULL)
throw "Bad type"; throw JSONException("Node is not an array type");
else else
_type = JSON_ARRAY; _type = JSON_ARRAY;
} }
@ -63,7 +75,7 @@ public:
{ {
if (_type != JSON_ARRAY) { if (_type != JSON_ARRAY) {
if (_type != JSON_NULL) if (_type != JSON_NULL)
throw "Bad type"; throw JSONException("Node is not an array type");
else else
_type = JSON_ARRAY; _type = JSON_ARRAY;
} }
@ -77,7 +89,7 @@ public:
{ {
if (_type != JSON_OBJECT) { if (_type != JSON_OBJECT) {
if (_type != JSON_NULL) if (_type != JSON_NULL)
throw "Bad type"; throw JSONException("Node is not an object type");
else else
_type = JSON_OBJECT; _type = JSON_OBJECT;
} }
@ -95,7 +107,7 @@ public:
{ {
if (_type != JSON_OBJECT) { if (_type != JSON_OBJECT) {
if (_type != JSON_NULL) if (_type != JSON_NULL)
throw "Bad type"; throw JSONException("Node is not an object type");
else else
_type = JSON_OBJECT; _type = JSON_OBJECT;
} }
@ -112,7 +124,7 @@ public:
bool GetBool() bool GetBool()
{ {
if (_type != JSON_BOOL) if (_type != JSON_BOOL)
throw "Bad type"; throw JSONException("Node is not a bool type");
return boost::get<bool>(_val); return boost::get<bool>(_val);
} }
@ -126,7 +138,7 @@ public:
int64 GetInt() int64 GetInt()
{ {
if (_type != JSON_INTEGER) if (_type != JSON_INTEGER)
throw "Bad type"; throw JSONException("Node is not an int type");
return boost::get<int64>(_val); return boost::get<int64>(_val);
} }
@ -140,7 +152,7 @@ public:
double GetDouble() double GetDouble()
{ {
if (_type != JSON_DOUBLE) if (_type != JSON_DOUBLE)
throw "Bad type"; throw JSONException("Node is not a double type");
return boost::get<double>(_val); return boost::get<double>(_val);
} }
@ -154,7 +166,7 @@ public:
std::string GetString() std::string GetString()
{ {
if (_type != JSON_STRING) if (_type != JSON_STRING)
throw "Bad type"; throw JSONException("Node is not a string type");
return boost::get<std::string>(_val); return boost::get<std::string>(_val);
} }
@ -188,7 +200,7 @@ public:
else if (_type == JSON_OBJECT) else if (_type == JSON_OBJECT)
return _map.size(); return _map.size();
else else
throw "Type has no size"; return 0;
} }
JSONValueType GetType() JSONValueType GetType()
@ -227,7 +239,7 @@ inline void JSON::Add(JSON& node)
{ {
if (_type != JSON_ARRAY) { if (_type != JSON_ARRAY) {
if (_type != JSON_NULL) if (_type != JSON_NULL)
throw "Bad type"; throw JSONException("Node is not an array type");
else else
_type = JSON_ARRAY; _type = JSON_ARRAY;
} }

3
src/server/shared/JSON/JSONReader.h

@ -176,7 +176,8 @@ namespace JSONReader
std::string::const_iterator begin = str.begin(); std::string::const_iterator begin = str.begin();
std::string::const_iterator end = str.end(); std::string::const_iterator end = str.end();
qi::phrase_parse(begin, end, g, ascii::space); if (!qi::phrase_parse(begin, end, g, ascii::space))
throw JSONException("Failed to parse JSON");
} }
} }

35
src/server/shared/JSONRPC/JSONRPC.cpp

@ -30,10 +30,9 @@ bool JSONRPC::Connect(JSONRPCConnectionInfo connInfo)
_sock.connect(_ep, error); _sock.connect(_ep, error);
} }
if (error) if (error) {
{ _sock.close();
sLog.Error(LOG_JSONRPC, "JSONRPC::Connect(): Error connecting to '%s': %s", _connInfo.Host.c_str(), boost::system::system_error(error).what()); throw JSONRPCException(Util::FS("JSONRPC::Connect(): Error connecting to '%s': %s", _connInfo.Host.c_str(), boost::system::system_error(error).what()));
return false;
} }
_sock.close(); _sock.close();
@ -58,10 +57,9 @@ JSON JSONRPC::Query(std::string method, JSON params)
_sock.close(); _sock.close();
_sock.connect(_ep, error); _sock.connect(_ep, error);
if (error) if (error) {
{ _sock.close();
sLog.Error(LOG_JSONRPC, "JSONRPC::Query(): Error connecting to '%s': %s", _connInfo.Host.c_str(), boost::system::system_error(error).what()); throw JSONRPCException(Util::FS("JSONRPC::Connect(): Error connecting to '%s': %s", _connInfo.Host.c_str(), boost::system::system_error(error).what()));
throw "fail";
} }
boost::asio::streambuf request_buf; boost::asio::streambuf request_buf;
@ -88,16 +86,14 @@ JSON JSONRPC::Query(std::string method, JSON params)
std::getline(response_stream, status_message); std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/") if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
{ _sock.close();
sLog.Error(LOG_JSONRPC, "JSONRPC::Query(): Malformed HTTP Response"); throw JSONRPCException("JSONRPC::Query(): Malformed HTTP Response");
throw "fail";
} }
if (status_code != 200) if (status_code != 200) {
{ _sock.close();
sLog.Error(LOG_JSONRPC, "JSONRPC::Query(): Returned status code: %u", status_code); throw JSONRPCException(Util::FS("JSONRPC::Query(): Returned status code: %u", status_code));
throw "fail";
} }
std::vector<std::string> headers; std::vector<std::string> headers;
@ -116,13 +112,6 @@ JSON JSONRPC::Query(std::string method, JSON params)
jsonresponse += oss.str(); jsonresponse += oss.str();
} }
// Read until EOF, writing data to output as we go.
/*while (_sock.available() && boost::asio::read(_sock, response, error)){
std::ostringstream oss;
oss << &response;
jsonresponse += oss.str();
}*/
_sock.close(); _sock.close();
sLog.Debug(LOG_JSONRPC, "JSONRPC::Query(): JSON Response: %s", jsonresponse.c_str()); sLog.Debug(LOG_JSONRPC, "JSONRPC::Query(): JSON Response: %s", jsonresponse.c_str());

9
src/server/shared/JSONRPC/JSONRPC.h

@ -6,7 +6,9 @@
#include <boost/cstdint.hpp> #include <boost/cstdint.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <string> #include <string>
#include "JSON.h" #include "JSON.h"
#include "Exception.h"
struct JSONRPCConnectionInfo struct JSONRPCConnectionInfo
{ {
@ -17,6 +19,13 @@ struct JSONRPCConnectionInfo
std::string B64Auth; std::string B64Auth;
}; };
class JSONRPCException: public Exception
{
public:
JSONRPCException(const char *text): Exception(text) {}
JSONRPCException(std::string text): Exception(text) {}
};
class JSONRPC class JSONRPC
{ {
public: public:

10
src/server/shared/MySQL/DatabaseConnection.cpp

@ -1,5 +1,6 @@
#include "DatabaseConnection.h" #include "DatabaseConnection.h"
#include "Log.h" #include "Log.h"
#include "Util.h"
namespace MySQL namespace MySQL
{ {
@ -28,10 +29,7 @@ namespace MySQL
MYSQL* mysqlInit; MYSQL* mysqlInit;
mysqlInit = mysql_init(NULL); mysqlInit = mysql_init(NULL);
if (!mysqlInit) if (!mysqlInit)
{ throw ConnectionException(Util::FS("Could not initialize Mysql connection to database `%s`", _connectionInfo.DB.c_str()));
sLog.Error(LOG_DATABASE, "Could not initialize Mysql connection to database `%s`", _connectionInfo.DB.c_str());
return false;
}
mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8"); mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8");
@ -51,9 +49,9 @@ namespace MySQL
} }
else else
{ {
sLog.Error(LOG_DATABASE, "Could not connect to MySQL database at %s: %s\n", _connectionInfo.Host.c_str(), mysql_error(mysqlInit)); const char* error = mysql_error(mysqlInit);
mysql_close(mysqlInit); mysql_close(mysqlInit);
return false; throw ConnectionException(Util::FS("Could not connect to MySQL database at %s: %s", _connectionInfo.Host.c_str(), mysql_error(mysqlInit)));
} }
} }

15
src/server/shared/MySQL/DatabaseConnection.h

@ -1,14 +1,16 @@
#ifndef DATABASE_CONNECTION_MYSQL_H_ #ifndef DATABASE_CONNECTION_MYSQL_H_
#define DATABASE_CONNECTION_MYSQL_H_ #define DATABASE_CONNECTION_MYSQL_H_
#include <boost/thread.hpp>
#include <mysql.h>
#include <string>
#include "DatabaseOperation.h" #include "DatabaseOperation.h"
#include "DatabaseWorker.h" #include "DatabaseWorker.h"
#include "QueryResult.h" #include "QueryResult.h"
#include "PreparedStatement.h" #include "PreparedStatement.h"
#include "Log.h" #include "Log.h"
#include "Exception.h"
#include <boost/thread.hpp>
#include <mysql.h>
namespace MySQL namespace MySQL
{ {
@ -27,6 +29,13 @@ namespace MySQL
std::string Pass; std::string Pass;
std::string DB; std::string DB;
}; };
class ConnectionException: public Exception
{
public:
ConnectionException(const char *text): Exception(text) {}
ConnectionException(std::string text): Exception(text) {}
};
class DatabaseConnection class DatabaseConnection
{ {

20
src/server/shared/Util.cpp

@ -31,6 +31,26 @@ uint32 Util::Date(bool utc)
return diff.total_seconds(); return diff.total_seconds();
} }
std::string Util::FS(const char *str, ...)
{
va_list ap;
va_start(ap, str);
char text[MAX_FORMAT_LEN];
vsnprintf(text, MAX_FORMAT_LEN, str, ap);
va_end(ap);
return std::string(text);
}
std::vector<std::string> Util::Explode(std::string input, std::string delim)
{
std::vector<std::string> strs;
boost::split(strs, input, boost::is_any_of(delim));
return strs;
}
std::string Util::ToBase64(std::string input, bool linebreaks) std::string Util::ToBase64(std::string input, bool linebreaks)
{ {
uint32_t writePaddChars = (3 - input.length() % 3) % 3; uint32_t writePaddChars = (3 - input.length() % 3) % 3;

3
src/server/shared/Util.h

@ -14,11 +14,14 @@
#include <boost/archive/iterators/transform_width.hpp> #include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp> #include <boost/archive/iterators/insert_linebreaks.hpp>
#include <boost/archive/iterators/remove_whitespace.hpp> #include <boost/archive/iterators/remove_whitespace.hpp>
#include <boost/algorithm/string.hpp>
namespace Util namespace Util
{ {
std::string Date(const char* format, bool utc = false); std::string Date(const char* format, bool utc = false);
uint32 Date(bool utc = true); uint32 Date(bool utc = true);
std::string FS(const char *str, ...);
std::vector<std::string> Explode(std::string input, std::string delim);
template <typename T> template <typename T>
class SynchronisedQueue class SynchronisedQueue

Loading…
Cancel
Save