#ifndef STRATUM_CLIENT_H_ #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 #include #include #include using namespace boost; using namespace boost::asio::ip; namespace Stratum { class Server; class Client : public boost::enable_shared_from_this { public: Client(Server* server, asio::io_service& io_service, uint64 id) : _io_service(io_service), _server(server), _socket(io_service), _ioStrand(io_service), _id(id), _subscribed(false), _jobid(0), _shareLimiter(this) { _diff = sConfig.Get("RetargetStartingDiff"); _minDiff = sConfig.Get("RetargetMinDiff"); } ~Client() { CleanJobs(); sLog.Info(LOG_STRATUM, "%u: I'm going out! Cya!", _ip); } tcp::socket& GetSocket() { return _socket; } // Start client up! bool Start(); void StartRead() { // Read until newline asio::async_read( _socket, _recvBuffer, asio::transfer_at_least(1), _ioStrand.wrap(boost::bind(&Client::_OnReceive, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred))); } void SendJob(bool clean); void SendMessage(JSON msg) { boost::unique_lock lock(_mtxWrite); std::string data = msg.ToString(); data += '\n'; sLog.Debug(LOG_SERVER, "Sending: %s", data.c_str()); boost::asio::async_write( _socket, boost::asio::buffer(data.c_str(), data.length()), _ioStrand.wrap(boost::bind(&Client::_OnSend, shared_from_this(), boost::asio::placeholders::error))); } void OnMiningSubmit(JSON msg); void OnMiningSubscribe(JSON msg); void OnMiningAuthorize(JSON msg); void OnMessage(JSON msg) { std::string method = msg["method"].GetString(); sLog.Debug(LOG_SERVER, "Decoded: %s", msg.ToString().c_str()); if (method.compare("mining.submit") == 0) OnMiningSubmit(msg); else if (method.compare("mining.subscribe") == 0) OnMiningSubscribe(msg); else if (method.compare("mining.authorize") == 0) OnMiningAuthorize(msg); else sLog.Debug(LOG_STRATUM, "Method '%s' not found.", method.c_str()); } // Gets new job from the server Job* GetJob(); void CleanJobs() { for (std::map::iterator it = _jobs.begin(); it != _jobs.end(); ++it) delete it->second; _jobs.clear(); } // Worker difficulty double GetDifficulty() { return _diff; } void SetDifficulty(double diff, bool resendJob = false) { if (diff < _minDiff) diff = _minDiff; if (diff == _diff) return; _diff = diff; // Send difficulty update JSON params; params.Add(double(_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; } uint32 GetIP() { return _ip; } void Ban(uint32 time); void Disconnect(); void RedirectGetwork() { sLog.Info(LOG_STRATUM, "Sending redirect to stratum for client %u", _ip); std::string redirect(Util::FS("HTTP/1.1 200 OK\r\nX-Stratum: stratum+tcp://%s:%u\r\nConnection: Close\r\nContent-Length: 41\r\n\r\n{\"error\": null, \"result\": false, \"id\": 0}\n", sConfig.Get("StratumRedirectHost").c_str(), sConfig.Get("StratumPort"))); boost::asio::async_write( _socket, boost::asio::buffer(redirect.c_str(), redirect.length()), _ioStrand.wrap(boost::bind(&Client::_OnSend, shared_from_this(), boost::asio::placeholders::error))); } void CloseSocket() { boost::system::error_code ec; _socket.shutdown(asio::ip::tcp::socket::shutdown_both, ec); _socket.close(ec); } public: void _OnReceive(const boost::system::error_code& error, size_t bytes_transferred); void _OnSend(const boost::system::error_code& error); private: // ASIO asio::io_service& _io_service; // Networking asio::streambuf _recvBuffer; std::string _recvMessage; tcp::socket _socket; asio::strand _ioStrand; boost::mutex _mtxWrite; uint32 _ip; uint64 _id; // Pointer to server Stratum::Server* _server; // Authorization std::set _workers; // Jobs bool _subscribed; uint32 _extranonce; std::map _jobs; uint32 _jobid; // Share limiting double _diff; double _minDiff; ShareLimiter _shareLimiter; }; typedef boost::shared_ptr ClientPtr; } #endif