mirror of https://github.com/GOSTSec/poolserver
Intel
11 years ago
31 changed files with 833 additions and 236 deletions
@ -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; |
@ -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 |
@ -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 |
@ -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)); |
||||||
|
} |
@ -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 |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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 |
@ -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 |
Loading…
Reference in new issue