diff --git a/.gitignore b/.gitignore index 2ca8849..01a7ff2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ CMakeLists.txt.user *.BACKUP.* *.BASE.* *.LOCAL.* +bin/ +etc/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ad8b92..0729664 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,10 +25,10 @@ set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) 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 -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 Libraries: ${Boost_LIBRARY_DIRS}") message(status "** Boost Libraries: ${Boost_LIBRARIES}") diff --git a/src/server/poolserver/CMakeLists.txt b/src/server/poolserver/CMakeLists.txt index c41bc49..9a56948 100644 --- a/src/server/poolserver/CMakeLists.txt +++ b/src/server/poolserver/CMakeLists.txt @@ -3,12 +3,16 @@ file(GLOB_RECURSE sources_Server Server/*.cpp Server/*.h) file(GLOB_RECURSE sources_Database Database/*.cpp Database/*.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) set(sources_Poolserver ${sources_Server} ${sources_Database} ${sources_Stratum} + ${sources_DataMgr} + ${sources_NetworkMgr} ${sources_localdir} ) @@ -16,6 +20,8 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Server ${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/Configuration ${CMAKE_SOURCE_DIR}/src/server/shared/MySQL @@ -46,7 +52,7 @@ target_link_libraries(poolserver # Install if(UNIX) install(TARGETS poolserver DESTINATION bin) - install(FILES poolserver.cfg.dist DESTINATION bin) + install(FILES poolserver.cfg.dist DESTINATION etc) elseif(WIN32) install(TARGETS poolserver DESTINATION "${CMAKE_INSTALL_PREFIX}") install(FILES poolserver.cfg.dist DESTINATION "${CMAKE_INSTALL_PREFIX}") diff --git a/src/server/poolserver/DataMgr/DataMgr.cpp b/src/server/poolserver/DataMgr/DataMgr.cpp new file mode 100644 index 0000000..e1b7279 --- /dev/null +++ b/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::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 sDataMgr; diff --git a/src/server/poolserver/DataMgr/DataMgr.h b/src/server/poolserver/DataMgr/DataMgr.h new file mode 100644 index 0000000..dbcc32d --- /dev/null +++ b/src/server/poolserver/DataMgr/DataMgr.h @@ -0,0 +1,47 @@ +#ifndef DATAMGR_H_ +#define DATAMGR_H_ + +#include +#include + +#include "Util.h" +#include "Share.h" + +#define BULK_MIN 1 +#define BULK_COUNT 50 + +template +class DataMgr +{ +public: + DataMgr() {} + + void Push(T data) + { + boost::unique_lock lock(_datamutex); + _datastore.push_back(data); + } + + T Pop() + { + boost::unique_lock lock(_datamutex); + Share share = _datastore.front(); + _datastore.pop_front(); + return share; + } + + size_t Size() + { + boost::unique_lock lock(_datamutex); + return _datastore.size(); + } + + void Upload(); +private: + boost::mutex _datamutex; + std::deque _datastore; +}; + +extern DataMgr sDataMgr; + +#endif diff --git a/src/server/poolserver/DataMgr/Share.h b/src/server/poolserver/DataMgr/Share.h new file mode 100644 index 0000000..9091fc6 --- /dev/null +++ b/src/server/poolserver/DataMgr/Share.h @@ -0,0 +1,22 @@ +#ifndef SHARE_H_ +#define SHARE_H_ + +#include + +#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 diff --git a/src/server/poolserver/Main.cpp b/src/server/poolserver/Main.cpp index f8ebbb6..8f9b3c1 100644 --- a/src/server/poolserver/Main.cpp +++ b/src/server/poolserver/Main.cpp @@ -26,15 +26,16 @@ bool InitConfig(int argc, char *argv[]) // Generic descGeneric.add_options() - ("version,v", "print version string") - ("help,h", "produce help message") - ("config,c", boost::program_options::value()->default_value("poolserver.cfg"),"name of a file of a configuration.") + ("version,v", "Print version string") + ("help,h", "Produce help message") + ("config,c", boost::program_options::value()->default_value("../etc/poolserver.cfg"),"Path to configuration file") ; // Server descServer.add_options() ("MinDiffTime", boost::program_options::value()->default_value(100), "Minimum server diff time") ("MiningAddress", boost::program_options::value()->default_value("n1w8gkPXdNGb6edm4vujBn71A72eQFCNCw"), "Address to send coins to") + ("BitcoinRPC", boost::program_options::value >()->multitoken(), "Bitcoin RPC login credentials") ; // Stratum @@ -42,13 +43,14 @@ bool InitConfig(int argc, char *argv[]) ("StratumHost,sh", boost::program_options::value()->default_value("0.0.0.0"), "Stratum server host") ("StratumPort,sp", boost::program_options::value()->default_value(3333), "Stratum server port") ("StratumBlockCheckTime", boost::program_options::value()->default_value(2000), "Time between block checks in ms") + ("StratumMinDifficulty", boost::program_options::value()->default_value(1), "The difficulty on which a new miner starts") ; // Logging descLogging.add_options() ("LogConsoleLevel", boost::program_options::value()->default_value(LOG_LEVEL_INFO), "Console log level (0-None, 1-Error, 2-Warn, 3-Info, 4-Debug)") ("LogConsoleDebugMask", boost::program_options::value()->default_value(0), "Console log debug mask") - ("LogFilePath", boost::program_options::value()->default_value("."), "File log path") + ("LogFilePath", boost::program_options::value()->default_value("../etc"), "File log path") ("LogFileLevel", boost::program_options::value()->default_value(LOG_LEVEL_WARN), "File log level (0-None, 1-Error, 2-Warn, 3-Info, 4-Debug)") ("LogFileDebugMask", boost::program_options::value()->default_value(0), "File log debug mask") ; @@ -88,6 +90,8 @@ bool InitConfig(int argc, char *argv[]) return true; } + sLog.Info(LOG_GENERAL, "Using config file: %s", sConfig.Get("config").c_str()); + store(parse_config_file(ifs, fileOptions), sConfig.vm); notify(sConfig.vm); @@ -102,7 +106,8 @@ int main(int argc, char *argv[]) sLog.OpenLogFile(sConfig.Get("LogFilePath")); 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(); delete server; diff --git a/src/server/poolserver/NetworkMgr/NetworkMgr.cpp b/src/server/poolserver/NetworkMgr/NetworkMgr.cpp new file mode 100644 index 0000000..9412f13 --- /dev/null +++ b/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 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("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 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)); +} diff --git a/src/server/poolserver/NetworkMgr/NetworkMgr.h b/src/server/poolserver/NetworkMgr/NetworkMgr.h new file mode 100644 index 0000000..dbbf89d --- /dev/null +++ b/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 +#include +#include +#include + +using namespace boost; + +typedef boost::function 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 _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 _cons; + + // Current block template + Bitcoin::BlockPtr _curBlockTmpl; + boost::mutex _mtxBlockTmpl; + + // ASIO + asio::io_service& _io_service; +}; + +#endif diff --git a/src/server/poolserver/Server/Server.cpp b/src/server/poolserver/Server/Server.cpp index 3e57925..f20a44c 100644 --- a/src/server/poolserver/Server/Server.cpp +++ b/src/server/poolserver/Server/Server.cpp @@ -6,22 +6,16 @@ #include "ServerDatabaseEnv.h" #include "Crypto.h" #include "Bitcoin.h" +#include "DataMgr.h" +#include "NetworkMgr.h" +#include "Exception.h" #include #include #include #include -struct Share -{ - 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) + +Server::Server(asio::io_service& io) : serverLoops(0), io_service(io), uploadtimer(io) { } @@ -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() { sLog.Info(LOG_SERVER, "Server is starting..."); InitDatabase(); - std::vector shares; + /*std::vector shares; sLog.Info(LOG_SERVER, "Loading shares..."); @@ -51,7 +54,7 @@ int Server::Run() sLog.Info(LOG_SERVER, "Min: %u Max: %u", min, max); 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); 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, "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()); + sLog.Info(LOG_SERVER, "Loaded %u shares", shares.size());*/ /*std::vector test = Util::ASCIIToBin("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); sLog.Info(LOG_SERVER, "Hash: %s", Util::BinToASCII(Crypto::SHA256D(test)).c_str()); @@ -103,31 +102,34 @@ int Server::Run() - // Main io service - asio::io_service io_service; + NetworkMgr::Initialize(io_service); - Stratum::Server srv(io_service); + std::vector btcrpc = sConfig.Get >("BitcoinRPC"); + for (int i = 0; i < btcrpc.size(); ++i) { + std::vector 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 - /*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); + Stratum::Server srv(io_service); // Start stratum server tcp::endpoint endpoint(tcp::v4(), sConfig.Get("StratumPort")); 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(); @@ -182,7 +184,7 @@ int Server::Run() // Mercy for CPU if (diff < minDiffTime+sleepDuration) { sleepDuration = minDiffTime - diff + sleepDuration; - boost::this_thread::sleep_for(boost::chrono::milliseconds(sleepDuration)); + boost::this_thread::sleep(boost::posix_time::milliseconds(sleepDuration)); } else sleepDuration = 0; diff --git a/src/server/poolserver/Server/Server.h b/src/server/poolserver/Server/Server.h index 941803a..bf270ac 100644 --- a/src/server/poolserver/Server/Server.h +++ b/src/server/poolserver/Server/Server.h @@ -12,11 +12,12 @@ class Server { public: - Server(); + Server(asio::io_service& io); ~Server(); Stratum::Server* stratumServer; + void UploadShares(const boost::system::error_code& e); boost::chrono::steady_clock::time_point diffStart; bool running; uint64_t serverLoops; @@ -25,6 +26,9 @@ public: void Update(uint32_t); bool InitDatabase(); + + boost::asio::deadline_timer uploadtimer; + asio::io_service& io_service; }; #endif diff --git a/src/server/poolserver/Stratum/Client.cpp b/src/server/poolserver/Stratum/Client.cpp index 8159adc..3473517 100644 --- a/src/server/poolserver/Stratum/Client.cpp +++ b/src/server/poolserver/Stratum/Client.cpp @@ -1,6 +1,9 @@ #include "Server.h" #include "Client.h" #include "BigNum.h" +#include "DataMgr.h" +#include "ShareLimiter.h" +#include "Exception.h" #include namespace Stratum @@ -55,11 +58,23 @@ namespace Stratum { 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; ByteBuffer jobbuf(Util::ASCIIToBin(params[1].GetString())); jobbuf >> jobid; + // Check if such job exists if (!_jobs.count(jobid)) { JSON response; response["id"] = msg["id"]; @@ -72,25 +87,47 @@ namespace Stratum } // check username + std::string username = params[0].GetString(); BinaryData extranonce2 = Util::ASCIIToBin(params[2].GetString()); if (extranonce2.size() != 4) { 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; } ByteBuffer timebuf(Util::Reverse(Util::ASCIIToBin(params[3].GetString()))); if (timebuf.Size() != 4) { 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; } ByteBuffer noncebuf(Util::Reverse(Util::ASCIIToBin(params[4].GetString()))); if (noncebuf.Size() != 4) { 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; } - + // Get job Job& job = _jobs[jobid]; @@ -98,6 +135,13 @@ namespace Stratum share << extranonce2 << timebuf << noncebuf; if (!job.SubmitShare(share.Binary())) { 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; } @@ -140,12 +184,34 @@ namespace Stratum std::cout << "Criteria: " << criteria << 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 if (target <= criteria) { sLog.Info(LOG_SERVER, "We have found a block candidate!"); _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) @@ -192,6 +258,7 @@ namespace Stratum { Job job; job.block = _server->GetWork(); + job.diff = 1; // Serialize transaction ByteBuffer coinbasebuf; @@ -219,6 +286,11 @@ namespace Stratum iss << is.rdbuf(); sLog.Debug(LOG_SERVER, "Received: %s", iss.str().c_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(); } else { diff --git a/src/server/poolserver/Stratum/Client.h b/src/server/poolserver/Stratum/Client.h index f6b8027..5136396 100644 --- a/src/server/poolserver/Stratum/Client.h +++ b/src/server/poolserver/Stratum/Client.h @@ -2,10 +2,12 @@ #define STRATUM_CLIENT_H_ #include "Common.h" +#include "Config.h" #include "Log.h" #include "JSON.h" #include "Server.h" #include "Job.h" +#include "ShareLimiter.h" #include #include @@ -24,8 +26,10 @@ namespace Stratum class Client : public boost::enable_shared_from_this { 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("StratumMinDifficulty"); + _minDiff = _diff; } tcp::socket& GetSocket() @@ -35,6 +39,11 @@ namespace Stratum 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 StartRead(); } @@ -84,6 +93,36 @@ namespace Stratum // Gets new job from the server 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() { _socket.close(); @@ -96,6 +135,8 @@ namespace Stratum // Networking asio::streambuf _recvBuffer; tcp::socket _socket; + uint32 _ip; + uint64 _id; // Pointer to server Stratum::Server* _server; @@ -108,6 +149,11 @@ namespace Stratum uint32 _extranonce; std::map _jobs; uint32 _jobid; + + // Share limiting + uint64 _diff; + uint64 _minDiff; + ShareLimiter _shareLimiter; }; typedef boost::shared_ptr ClientPtr; diff --git a/src/server/poolserver/Stratum/Job.h b/src/server/poolserver/Stratum/Job.h index 6aaff78..2585b45 100644 --- a/src/server/poolserver/Stratum/Job.h +++ b/src/server/poolserver/Stratum/Job.h @@ -11,6 +11,7 @@ namespace Stratum class Job { public: + uint64 diff; Bitcoin::BlockPtr block; BinaryData coinbase1; BinaryData coinbase2; diff --git a/src/server/poolserver/Stratum/Server.cpp b/src/server/poolserver/Stratum/Server.cpp index f5f2f87..1a70e88 100644 --- a/src/server/poolserver/Stratum/Server.cpp +++ b/src/server/poolserver/Stratum/Server.cpp @@ -4,9 +4,6 @@ namespace Stratum { void Server::Start(tcp::endpoint endpoint) { - // Start block checking timer - _CheckBlocksTimer(); - _acceptor.open(endpoint.protocol()); _acceptor.set_option(tcp::acceptor::reuse_address(true)); _acceptor.bind(endpoint); @@ -24,33 +21,15 @@ namespace Stratum _io_service.post(boost::bind(&Client::SendMessage, (*it), msg)); } - void Server::ResetWork() + void Server::SendBlockTmpl(bool resetWork) { std::set::iterator 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) { - // Serialize 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; - } + return NetworkMgr::Instance()->SubmitBlock(block); } } diff --git a/src/server/poolserver/Stratum/Server.h b/src/server/poolserver/Stratum/Server.h index 32a93fe..c47d3e1 100644 --- a/src/server/poolserver/Stratum/Server.h +++ b/src/server/poolserver/Stratum/Server.h @@ -10,6 +10,7 @@ #include "Bitcoin.h" #include "Util.h" #include "ByteBuffer.h" +#include "NetworkMgr.h" #include #include @@ -27,12 +28,22 @@ namespace Stratum uint16 Port; }; + // Used for sorting std::set + struct ClientPtrCMP + { + bool operator() (const ClientPtr& a, const ClientPtr& b) + { + return a->GetID() < b->GetID(); + } + }; + class Server { 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("MiningAddress")); + // Subscribe for block updates + NetworkMgr::Instance()->BlockNotifyBind(boost::bind(&Server::BlockNotify, this, _1, _2)); } ~Server() @@ -45,11 +56,6 @@ namespace Stratum // Starts accepting connections void Start(tcp::endpoint endpoint); - void SetBitcoinRPC(JSONRPC* bitcoinrpc) - { - _bitcoinrpc = bitcoinrpc; - } - // Sends message to all clients void SendToAll(JSON msg); @@ -62,12 +68,22 @@ namespace Stratum // Returns current work Bitcoin::BlockPtr GetWork() { - boost::lock_guard guard(_mtx_workupdate); + boost::lock_guard guard(_mtxCurrentWork); 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 - void ResetWork(); + void SendBlockTmpl(bool resetWork); // Submits block to bitcoind bool SubmitBlock(Bitcoin::Block block); @@ -82,7 +98,7 @@ namespace Stratum private: 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)); } @@ -90,7 +106,7 @@ namespace Stratum void _OnAccept(ClientPtr client, const boost::system::error_code& 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(); _clients.insert(client); } else { @@ -99,117 +115,19 @@ namespace Stratum _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("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 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: // Network - std::set _clients; asio::io_service& _io_service; tcp::acceptor _acceptor; - // Mutexes - boost::mutex _mtx_checkblock; - boost::mutex _mtx_workupdate; - - // RPC - JSONRPC* _bitcoinrpc; - - // Bitcoin info - BinaryData _pubkey; - asio::deadline_timer _blockCheckTimer; - uint32 _blockHeight; + // Clients + std::set _clients; + uint64 _clientId; // Work Bitcoin::BlockPtr _currentWork; + boost::mutex _mtxCurrentWork; uint32 _extranonce; }; } diff --git a/src/server/poolserver/Stratum/ShareLimiter.cpp b/src/server/poolserver/Stratum/ShareLimiter.cpp new file mode 100644 index 0000000..567e0c5 --- /dev/null +++ b/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; + } +} diff --git a/src/server/poolserver/Stratum/ShareLimiter.h b/src/server/poolserver/Stratum/ShareLimiter.h new file mode 100644 index 0000000..6e56da5 --- /dev/null +++ b/src/server/poolserver/Stratum/ShareLimiter.h @@ -0,0 +1,44 @@ +#ifndef SHARELIMITER_H_ +#define SHARELIMITER_H_ + +#include "Common.h" +#include "Util.h" + +#include + +#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 _shares; + Client* _client; + uint64 _lastRetarget; + uint64 _startTime; + }; +} + +#endif diff --git a/src/server/poolserver/poolserver.cfg.dist b/src/server/poolserver/poolserver.cfg.dist index 613f56d..3d75dee 100644 --- a/src/server/poolserver/poolserver.cfg.dist +++ b/src/server/poolserver/poolserver.cfg.dist @@ -72,9 +72,9 @@ StratumPort=3333 # Description: Location to save logfile to # Important: Do not add trailing slash # Example: "/var/log" -# Default: "." - (Save log file next to executable) +# Default: "../etc" - (Save log file next to executable) -LogFilePath="." +LogFilePath="../etc" # # LogConsoleLevel @@ -144,7 +144,7 @@ MySQLPort=3306 # Description: MySQL Server Username # Default: "" - (No Username) -MySQLUser="" +MySQLUser="root" # # MySQLPass diff --git a/src/server/shared/Bitcoin/Bitcoin.h b/src/server/shared/Bitcoin/Bitcoin.h index 3909ee8..eadf469 100644 --- a/src/server/shared/Bitcoin/Bitcoin.h +++ b/src/server/shared/Bitcoin/Bitcoin.h @@ -30,6 +30,35 @@ namespace Bitcoin mpz_ui_pow_ui(power, 2, 8 * (nbytes - 3)); 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 diff --git a/src/server/shared/ByteBuffer.h b/src/server/shared/ByteBuffer.h index fb067c5..0895517 100644 --- a/src/server/shared/ByteBuffer.h +++ b/src/server/shared/ByteBuffer.h @@ -69,7 +69,7 @@ public: size_t size = sizeof(T); if (vec.size() < pointer+size) - return NULL; + return 0; T data = 0; for (uint64 i = 0; i < size; ++i) diff --git a/src/server/shared/Common.h b/src/server/shared/Common.h index 8fdbd30..e08f634 100644 --- a/src/server/shared/Common.h +++ b/src/server/shared/Common.h @@ -3,6 +3,11 @@ #include #include +#include +#include +#include + +#define MAX_FORMAT_LEN 32*1024 typedef uint8_t uint8; typedef uint16_t uint16; diff --git a/src/server/shared/Exception.h b/src/server/shared/Exception.h new file mode 100644 index 0000000..3770aca --- /dev/null +++ b/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 diff --git a/src/server/shared/JSON/JSON.h b/src/server/shared/JSON/JSON.h index cb1db6b..cee5e23 100644 --- a/src/server/shared/JSON/JSON.h +++ b/src/server/shared/JSON/JSON.h @@ -2,6 +2,9 @@ #define JSON_H_ #include "Common.h" +#include "Log.h" +#include "Util.h" +#include "Exception.h" #include #include #include @@ -10,7 +13,6 @@ #include #include #include -#include enum JSONValueType { @@ -25,6 +27,13 @@ enum JSONValueType typedef boost::variant JSONValue; +class JSONException: public Exception +{ +public: + JSONException(const char *text): Exception(text) {} + JSONException(std::string text): Exception(text) {} +}; + class JSON { public: @@ -41,6 +50,9 @@ public: // Arrays 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]; } @@ -49,7 +61,7 @@ public: { if (_type != JSON_ARRAY) { if (_type != JSON_NULL) - throw "Bad type"; + throw JSONException("Node is not an array type"); else _type = JSON_ARRAY; } @@ -63,7 +75,7 @@ public: { if (_type != JSON_ARRAY) { if (_type != JSON_NULL) - throw "Bad type"; + throw JSONException("Node is not an array type"); else _type = JSON_ARRAY; } @@ -77,7 +89,7 @@ public: { if (_type != JSON_OBJECT) { if (_type != JSON_NULL) - throw "Bad type"; + throw JSONException("Node is not an object type"); else _type = JSON_OBJECT; } @@ -95,7 +107,7 @@ public: { if (_type != JSON_OBJECT) { if (_type != JSON_NULL) - throw "Bad type"; + throw JSONException("Node is not an object type"); else _type = JSON_OBJECT; } @@ -112,7 +124,7 @@ public: bool GetBool() { if (_type != JSON_BOOL) - throw "Bad type"; + throw JSONException("Node is not a bool type"); return boost::get(_val); } @@ -126,7 +138,7 @@ public: int64 GetInt() { if (_type != JSON_INTEGER) - throw "Bad type"; + throw JSONException("Node is not an int type"); return boost::get(_val); } @@ -140,7 +152,7 @@ public: double GetDouble() { if (_type != JSON_DOUBLE) - throw "Bad type"; + throw JSONException("Node is not a double type"); return boost::get(_val); } @@ -154,7 +166,7 @@ public: std::string GetString() { if (_type != JSON_STRING) - throw "Bad type"; + throw JSONException("Node is not a string type"); return boost::get(_val); } @@ -188,7 +200,7 @@ public: else if (_type == JSON_OBJECT) return _map.size(); else - throw "Type has no size"; + return 0; } JSONValueType GetType() @@ -227,7 +239,7 @@ inline void JSON::Add(JSON& node) { if (_type != JSON_ARRAY) { if (_type != JSON_NULL) - throw "Bad type"; + throw JSONException("Node is not an array type"); else _type = JSON_ARRAY; } diff --git a/src/server/shared/JSON/JSONReader.h b/src/server/shared/JSON/JSONReader.h index 6b3938e..1af48a0 100644 --- a/src/server/shared/JSON/JSONReader.h +++ b/src/server/shared/JSON/JSONReader.h @@ -176,7 +176,8 @@ namespace JSONReader std::string::const_iterator begin = str.begin(); 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"); } } diff --git a/src/server/shared/JSONRPC/JSONRPC.cpp b/src/server/shared/JSONRPC/JSONRPC.cpp index 32a9e95..21d65b2 100644 --- a/src/server/shared/JSONRPC/JSONRPC.cpp +++ b/src/server/shared/JSONRPC/JSONRPC.cpp @@ -30,10 +30,9 @@ bool JSONRPC::Connect(JSONRPCConnectionInfo connInfo) _sock.connect(_ep, error); } - if (error) - { - sLog.Error(LOG_JSONRPC, "JSONRPC::Connect(): Error connecting to '%s': %s", _connInfo.Host.c_str(), boost::system::system_error(error).what()); - return false; + if (error) { + _sock.close(); + throw JSONRPCException(Util::FS("JSONRPC::Connect(): Error connecting to '%s': %s", _connInfo.Host.c_str(), boost::system::system_error(error).what())); } _sock.close(); @@ -58,10 +57,9 @@ JSON JSONRPC::Query(std::string method, JSON params) _sock.close(); _sock.connect(_ep, error); - if (error) - { - sLog.Error(LOG_JSONRPC, "JSONRPC::Query(): Error connecting to '%s': %s", _connInfo.Host.c_str(), boost::system::system_error(error).what()); - throw "fail"; + if (error) { + _sock.close(); + throw JSONRPCException(Util::FS("JSONRPC::Connect(): Error connecting to '%s': %s", _connInfo.Host.c_str(), boost::system::system_error(error).what())); } boost::asio::streambuf request_buf; @@ -88,16 +86,14 @@ JSON JSONRPC::Query(std::string method, JSON params) std::getline(response_stream, status_message); - if (!response_stream || http_version.substr(0, 5) != "HTTP/") - { - sLog.Error(LOG_JSONRPC, "JSONRPC::Query(): Malformed HTTP Response"); - throw "fail"; + if (!response_stream || http_version.substr(0, 5) != "HTTP/") { + _sock.close(); + throw JSONRPCException("JSONRPC::Query(): Malformed HTTP Response"); } - if (status_code != 200) - { - sLog.Error(LOG_JSONRPC, "JSONRPC::Query(): Returned status code: %u", status_code); - throw "fail"; + if (status_code != 200) { + _sock.close(); + throw JSONRPCException(Util::FS("JSONRPC::Query(): Returned status code: %u", status_code)); } std::vector headers; @@ -116,13 +112,6 @@ JSON JSONRPC::Query(std::string method, JSON params) 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(); sLog.Debug(LOG_JSONRPC, "JSONRPC::Query(): JSON Response: %s", jsonresponse.c_str()); diff --git a/src/server/shared/JSONRPC/JSONRPC.h b/src/server/shared/JSONRPC/JSONRPC.h index c9043de..7c7661a 100644 --- a/src/server/shared/JSONRPC/JSONRPC.h +++ b/src/server/shared/JSONRPC/JSONRPC.h @@ -6,7 +6,9 @@ #include #include #include + #include "JSON.h" +#include "Exception.h" struct JSONRPCConnectionInfo { @@ -17,6 +19,13 @@ struct JSONRPCConnectionInfo std::string B64Auth; }; +class JSONRPCException: public Exception +{ +public: + JSONRPCException(const char *text): Exception(text) {} + JSONRPCException(std::string text): Exception(text) {} +}; + class JSONRPC { public: diff --git a/src/server/shared/MySQL/DatabaseConnection.cpp b/src/server/shared/MySQL/DatabaseConnection.cpp index 49678b1..adc6dbc 100644 --- a/src/server/shared/MySQL/DatabaseConnection.cpp +++ b/src/server/shared/MySQL/DatabaseConnection.cpp @@ -1,5 +1,6 @@ #include "DatabaseConnection.h" #include "Log.h" +#include "Util.h" namespace MySQL { @@ -28,10 +29,7 @@ namespace MySQL MYSQL* mysqlInit; mysqlInit = mysql_init(NULL); if (!mysqlInit) - { - sLog.Error(LOG_DATABASE, "Could not initialize Mysql connection to database `%s`", _connectionInfo.DB.c_str()); - return false; - } + throw ConnectionException(Util::FS("Could not initialize Mysql connection to database `%s`", _connectionInfo.DB.c_str())); mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8"); @@ -51,9 +49,9 @@ namespace MySQL } 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); - return false; + throw ConnectionException(Util::FS("Could not connect to MySQL database at %s: %s", _connectionInfo.Host.c_str(), mysql_error(mysqlInit))); } } diff --git a/src/server/shared/MySQL/DatabaseConnection.h b/src/server/shared/MySQL/DatabaseConnection.h index 3bec214..f6541ce 100644 --- a/src/server/shared/MySQL/DatabaseConnection.h +++ b/src/server/shared/MySQL/DatabaseConnection.h @@ -1,14 +1,16 @@ #ifndef DATABASE_CONNECTION_MYSQL_H_ #define DATABASE_CONNECTION_MYSQL_H_ +#include +#include +#include + #include "DatabaseOperation.h" #include "DatabaseWorker.h" #include "QueryResult.h" #include "PreparedStatement.h" #include "Log.h" - -#include -#include +#include "Exception.h" namespace MySQL { @@ -27,6 +29,13 @@ namespace MySQL std::string Pass; std::string DB; }; + + class ConnectionException: public Exception + { + public: + ConnectionException(const char *text): Exception(text) {} + ConnectionException(std::string text): Exception(text) {} + }; class DatabaseConnection { diff --git a/src/server/shared/Util.cpp b/src/server/shared/Util.cpp index 9fed2ac..bd7b251 100644 --- a/src/server/shared/Util.cpp +++ b/src/server/shared/Util.cpp @@ -31,6 +31,26 @@ uint32 Util::Date(bool utc) 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 Util::Explode(std::string input, std::string delim) +{ + std::vector strs; + boost::split(strs, input, boost::is_any_of(delim)); + return strs; +} + std::string Util::ToBase64(std::string input, bool linebreaks) { uint32_t writePaddChars = (3 - input.length() % 3) % 3; diff --git a/src/server/shared/Util.h b/src/server/shared/Util.h index 06e58be..9dfbee1 100644 --- a/src/server/shared/Util.h +++ b/src/server/shared/Util.h @@ -14,11 +14,14 @@ #include #include #include +#include namespace Util { std::string Date(const char* format, bool utc = false); uint32 Date(bool utc = true); + std::string FS(const char *str, ...); + std::vector Explode(std::string input, std::string delim); template class SynchronisedQueue