Genoil
9 years ago
11 changed files with 515 additions and 98 deletions
@ -0,0 +1,332 @@ |
|||||
|
|
||||
|
#include "EthStratumClientV2.h" |
||||
|
#include <libdevcore/Log.h> |
||||
|
using boost::asio::ip::tcp; |
||||
|
|
||||
|
|
||||
|
EthStratumClientV2::EthStratumClientV2(GenericFarm<EthashProofOfWork> * f, MinerType m, string const & host, string const & port, string const & user, string const & pass, int const & retries, int const & worktimeout) |
||||
|
: Worker("stratum"), |
||||
|
m_socket(m_io_service) |
||||
|
{ |
||||
|
m_minerType = m; |
||||
|
m_primary.host = host; |
||||
|
m_primary.port = port; |
||||
|
m_primary.user = user; |
||||
|
m_primary.pass = pass; |
||||
|
|
||||
|
p_active = &m_primary; |
||||
|
|
||||
|
m_authorized = false; |
||||
|
m_connected = false; |
||||
|
m_maxRetries = retries; |
||||
|
m_worktimeout = worktimeout; |
||||
|
|
||||
|
p_farm = f; |
||||
|
p_worktimer = nullptr; |
||||
|
startWorking(); |
||||
|
} |
||||
|
|
||||
|
EthStratumClientV2::~EthStratumClientV2() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void EthStratumClientV2::setFailover(string const & host, string const & port) |
||||
|
{ |
||||
|
setFailover(host, port, p_active->user, p_active->pass); |
||||
|
} |
||||
|
|
||||
|
void EthStratumClientV2::setFailover(string const & host, string const & port, string const & user, string const & pass) |
||||
|
{ |
||||
|
m_failover.host = host; |
||||
|
m_failover.port = port; |
||||
|
m_failover.user = user; |
||||
|
m_failover.pass = pass; |
||||
|
} |
||||
|
|
||||
|
void EthStratumClientV2::workLoop() |
||||
|
{ |
||||
|
while (true) |
||||
|
{ |
||||
|
if (!m_connected) |
||||
|
{ |
||||
|
m_io_service.run(); |
||||
|
connect(); |
||||
|
} |
||||
|
read_until(m_socket, m_responseBuffer, "\n"); |
||||
|
std::istream is(&m_responseBuffer); |
||||
|
std::string response; |
||||
|
getline(is, response); |
||||
|
|
||||
|
if (response.front() == '{' && response.back() == '}') |
||||
|
{ |
||||
|
Json::Value responseObject; |
||||
|
Json::Reader reader; |
||||
|
if (reader.parse(response.c_str(), responseObject)) |
||||
|
{ |
||||
|
processReponse(responseObject); |
||||
|
m_response = response; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
cwarn << "Parse response failed: " << reader.getFormattedErrorMessages(); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
cwarn << "Discarding incomplete response"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void EthStratumClientV2::connect() |
||||
|
{ |
||||
|
cnote << "Connecting to stratum server " << p_active->host + ":" + p_active->port; |
||||
|
|
||||
|
tcp::resolver r(m_io_service); |
||||
|
tcp::resolver::query q(p_active->host, p_active->port); |
||||
|
tcp::resolver::iterator endpoint_iterator = r.resolve(q); |
||||
|
tcp::resolver::iterator end; |
||||
|
|
||||
|
boost::system::error_code error = boost::asio::error::host_not_found; |
||||
|
while (error && endpoint_iterator != end) |
||||
|
{ |
||||
|
m_socket.close(); |
||||
|
m_socket.connect(*endpoint_iterator++, error); |
||||
|
} |
||||
|
if (error) |
||||
|
{ |
||||
|
cerr << "Could not connect to stratum server " << p_active->host + ":" + p_active->port + ", " << error.message(); |
||||
|
reconnect(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
cnote << "Connected!"; |
||||
|
m_connected = true; |
||||
|
if (!p_farm->isMining()) |
||||
|
{ |
||||
|
cnote << "Starting farm"; |
||||
|
if (m_minerType == MinerType::CPU) |
||||
|
p_farm->start("cpu"); |
||||
|
else if (m_minerType == MinerType::CL) |
||||
|
p_farm->start("opencl"); |
||||
|
else if (m_minerType == MinerType::CUDA) |
||||
|
p_farm->start("cuda"); |
||||
|
} |
||||
|
std::ostream os(&m_requestBuffer); |
||||
|
os << "{\"id\": 1, \"method\": \"mining.subscribe\", \"params\": []}\n"; |
||||
|
write(m_socket, m_requestBuffer); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#define BOOST_ASIO_ENABLE_CANCELIO |
||||
|
|
||||
|
void EthStratumClientV2::reconnect() |
||||
|
{ |
||||
|
if (p_worktimer) { |
||||
|
p_worktimer->cancel(); |
||||
|
p_worktimer = nullptr; |
||||
|
} |
||||
|
|
||||
|
m_io_service.reset(); |
||||
|
//m_socket.close(); // leads to crashes on Linux
|
||||
|
m_authorized = false; |
||||
|
m_connected = false; |
||||
|
|
||||
|
if (!m_failover.host.empty()) |
||||
|
{ |
||||
|
m_retries++; |
||||
|
|
||||
|
if (m_retries > m_maxRetries) |
||||
|
{ |
||||
|
if (m_failover.host == "exit") { |
||||
|
disconnect(); |
||||
|
return; |
||||
|
} |
||||
|
else if (p_active == &m_primary) |
||||
|
{ |
||||
|
p_active = &m_failover; |
||||
|
} |
||||
|
else { |
||||
|
p_active = &m_primary; |
||||
|
} |
||||
|
m_retries = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
cnote << "Reconnecting in 3 seconds..."; |
||||
|
boost::asio::deadline_timer timer(m_io_service, boost::posix_time::seconds(3)); |
||||
|
timer.wait(); |
||||
|
|
||||
|
connect(); |
||||
|
} |
||||
|
|
||||
|
void EthStratumClientV2::disconnect() |
||||
|
{ |
||||
|
cnote << "Disconnecting"; |
||||
|
m_connected = false; |
||||
|
m_running = false; |
||||
|
if (p_farm->isMining()) |
||||
|
{ |
||||
|
cnote << "Stopping farm"; |
||||
|
p_farm->stop(); |
||||
|
} |
||||
|
m_socket.close(); |
||||
|
m_io_service.stop(); |
||||
|
} |
||||
|
|
||||
|
void EthStratumClientV2::processReponse(Json::Value& responseObject) |
||||
|
{ |
||||
|
Json::Value error = responseObject.get("error", new Json::Value); |
||||
|
if (error.isArray()) |
||||
|
{ |
||||
|
string msg = error.get(1, "Unknown error").asString(); |
||||
|
cnote << msg; |
||||
|
} |
||||
|
std::ostream os(&m_requestBuffer); |
||||
|
Json::Value params; |
||||
|
int id = responseObject.get("id", Json::Value::null).asInt(); |
||||
|
switch (id) |
||||
|
{ |
||||
|
case 1: |
||||
|
cnote << "Subscribed to stratum server"; |
||||
|
|
||||
|
os << "{\"id\": 2, \"method\": \"mining.authorize\", \"params\": [\"" << p_active->user << "\",\"" << p_active->pass << "\"]}\n"; |
||||
|
|
||||
|
write(m_socket, m_requestBuffer); |
||||
|
break; |
||||
|
case 2: |
||||
|
m_authorized = responseObject.get("result", Json::Value::null).asBool(); |
||||
|
if (!m_authorized) |
||||
|
{ |
||||
|
cnote << "Worker not authorized:" << p_active->user; |
||||
|
disconnect(); |
||||
|
return; |
||||
|
} |
||||
|
cnote << "Authorized worker " << p_active->user; |
||||
|
break; |
||||
|
case 4: |
||||
|
if (responseObject.get("result", false).asBool()) { |
||||
|
cnote << "B-) Submitted and accepted."; |
||||
|
p_farm->acceptedSolution(m_stale); |
||||
|
} |
||||
|
else { |
||||
|
cwarn << ":-( Not accepted."; |
||||
|
p_farm->rejectedSolution(m_stale); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
string method = responseObject.get("method", "").asString(); |
||||
|
if (method == "mining.notify") |
||||
|
{ |
||||
|
params = responseObject.get("params", Json::Value::null); |
||||
|
if (params.isArray()) |
||||
|
{ |
||||
|
string job = params.get((Json::Value::ArrayIndex)0, "").asString(); |
||||
|
string sHeaderHash = params.get((Json::Value::ArrayIndex)1, "").asString(); |
||||
|
string sSeedHash = params.get((Json::Value::ArrayIndex)2, "").asString(); |
||||
|
string sShareTarget = params.get((Json::Value::ArrayIndex)3, "").asString(); |
||||
|
//bool cleanJobs = params.get((Json::Value::ArrayIndex)4, "").asBool();
|
||||
|
|
||||
|
// coinmine.pl fix
|
||||
|
int l = sShareTarget.length(); |
||||
|
if (l < 66) |
||||
|
sShareTarget = "0x" + string(66 - l, '0') + sShareTarget.substr(2); |
||||
|
|
||||
|
|
||||
|
if (sHeaderHash != "" && sSeedHash != "" && sShareTarget != "") |
||||
|
{ |
||||
|
cnote << "Received new job #" + job.substr(0,8); |
||||
|
//cnote << "Header hash: " + sHeaderHash;
|
||||
|
//cnote << "Seed hash: " + sSeedHash;
|
||||
|
//cnote << "Share target: " + sShareTarget;
|
||||
|
|
||||
|
h256 seedHash = h256(sSeedHash); |
||||
|
h256 headerHash = h256(sHeaderHash); |
||||
|
|
||||
|
if (headerHash != m_current.headerHash) |
||||
|
{ |
||||
|
//x_current.lock();
|
||||
|
if (p_worktimer) |
||||
|
p_worktimer->cancel(); |
||||
|
|
||||
|
m_previous.headerHash = m_current.headerHash; |
||||
|
m_previous.seedHash = m_current.seedHash; |
||||
|
m_previous.boundary = m_current.boundary; |
||||
|
m_previousJob = m_job; |
||||
|
|
||||
|
m_current.headerHash = h256(sHeaderHash); |
||||
|
m_current.seedHash = seedHash; |
||||
|
m_current.boundary = h256(sShareTarget);// , h256::AlignRight);
|
||||
|
m_job = job; |
||||
|
|
||||
|
p_farm->setWork(m_current); |
||||
|
//x_current.unlock();
|
||||
|
p_worktimer = new boost::asio::deadline_timer(m_io_service, boost::posix_time::seconds(m_worktimeout)); |
||||
|
p_worktimer->async_wait(boost::bind(&EthStratumClientV2::work_timeout_handler, this, boost::asio::placeholders::error)); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else if (method == "mining.set_difficulty") |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
else if (method == "client.get_version") |
||||
|
{ |
||||
|
os << "{\"error\": null, \"id\" : " << id << ", \"result\" : \"" << ETH_PROJECT_VERSION << "\"}\n"; |
||||
|
write(m_socket, m_requestBuffer); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void EthStratumClientV2::work_timeout_handler(const boost::system::error_code& ec) { |
||||
|
if (!ec) { |
||||
|
cnote << "No new work received in" << m_worktimeout << "seconds."; |
||||
|
reconnect(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool EthStratumClientV2::submit(EthashProofOfWork::Solution solution) { |
||||
|
// x_current.lock();
|
||||
|
EthashProofOfWork::WorkPackage tempWork(m_current); |
||||
|
string temp_job = m_job; |
||||
|
EthashProofOfWork::WorkPackage tempPreviousWork(m_previous); |
||||
|
string temp_previous_job = m_previousJob; |
||||
|
// x_current.unlock();
|
||||
|
|
||||
|
cnote << "Solution found; Submitting to" << p_active->host << "..."; |
||||
|
cnote << " Nonce:" << "0x" + solution.nonce.hex(); |
||||
|
|
||||
|
if (EthashAux::eval(tempWork.seedHash, tempWork.headerHash, solution.nonce).value < tempWork.boundary) |
||||
|
{ |
||||
|
string json = "{\"id\": 4, \"method\": \"mining.submit\", \"params\": [\"" + p_active->user + "\",\"" + temp_job + "\",\"0x" + solution.nonce.hex() + "\",\"0x" + tempWork.headerHash.hex() + "\",\"0x" + solution.mixHash.hex() + "\"]}\n"; |
||||
|
std::ostream os(&m_requestBuffer); |
||||
|
os << json; |
||||
|
m_stale = false; |
||||
|
write(m_socket, m_requestBuffer); |
||||
|
return true; |
||||
|
} |
||||
|
else if (EthashAux::eval(tempPreviousWork.seedHash, tempPreviousWork.headerHash, solution.nonce).value < tempPreviousWork.boundary) |
||||
|
{ |
||||
|
string json = "{\"id\": 4, \"method\": \"mining.submit\", \"params\": [\"" + p_active->user + "\",\"" + temp_previous_job + "\",\"0x" + solution.nonce.hex() + "\",\"0x" + tempPreviousWork.headerHash.hex() + "\",\"0x" + solution.mixHash.hex() + "\"]}\n"; |
||||
|
std::ostream os(&m_requestBuffer); |
||||
|
os << json; |
||||
|
m_stale = true; |
||||
|
cwarn << "Submitting stale solution."; |
||||
|
write(m_socket, m_requestBuffer); |
||||
|
return true; |
||||
|
} |
||||
|
else { |
||||
|
m_stale = false; |
||||
|
cwarn << "FAILURE: GPU gave incorrect result!"; |
||||
|
p_farm->failedSolution(); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
@ -0,0 +1,83 @@ |
|||||
|
#include <iostream> |
||||
|
#include <boost/array.hpp> |
||||
|
#include <boost/asio.hpp> |
||||
|
#include <boost/bind.hpp> |
||||
|
#include <json/json.h> |
||||
|
#include <libdevcore/Log.h> |
||||
|
#include <libdevcore/FixedHash.h> |
||||
|
#include <libdevcore/Worker.h> |
||||
|
#include <libethcore/Farm.h> |
||||
|
#include <libethcore/EthashAux.h> |
||||
|
#include <libethcore/Miner.h> |
||||
|
|
||||
|
#include "BuildInfo.h" |
||||
|
|
||||
|
|
||||
|
using namespace std; |
||||
|
using namespace boost::asio; |
||||
|
using boost::asio::ip::tcp; |
||||
|
using namespace dev; |
||||
|
using namespace dev::eth; |
||||
|
|
||||
|
class EthStratumClientV2 : public Worker |
||||
|
{ |
||||
|
public: |
||||
|
EthStratumClientV2(GenericFarm<EthashProofOfWork> * f, MinerType m, string const & host, string const & port, string const & user, string const & pass, int const & retries, int const & worktimeout); |
||||
|
~EthStratumClientV2(); |
||||
|
|
||||
|
void setFailover(string const & host, string const & port); |
||||
|
void setFailover(string const & host, string const & port, string const & user, string const & pass); |
||||
|
|
||||
|
bool isRunning() { return m_running; } |
||||
|
bool isConnected() { return m_connected; } |
||||
|
h256 currentHeaderHash() { return m_current.headerHash; } |
||||
|
bool current() { return m_current; } |
||||
|
unsigned waitState() { return m_waitState; } |
||||
|
bool submit(EthashProofOfWork::Solution solution); |
||||
|
void reconnect(); |
||||
|
private: |
||||
|
void workLoop() override; |
||||
|
void connect(); |
||||
|
|
||||
|
void disconnect(); |
||||
|
void work_timeout_handler(const boost::system::error_code& ec); |
||||
|
|
||||
|
void processReponse(Json::Value& responseObject); |
||||
|
|
||||
|
MinerType m_minerType; |
||||
|
|
||||
|
cred_t * p_active; |
||||
|
cred_t m_primary; |
||||
|
cred_t m_failover; |
||||
|
|
||||
|
bool m_authorized; |
||||
|
bool m_connected; |
||||
|
bool m_running = true; |
||||
|
|
||||
|
int m_retries = 0; |
||||
|
int m_maxRetries; |
||||
|
int m_worktimeout = 60; |
||||
|
|
||||
|
int m_waitState = MINER_WAIT_STATE_WORK; |
||||
|
|
||||
|
string m_response; |
||||
|
|
||||
|
GenericFarm<EthashProofOfWork> * p_farm; |
||||
|
EthashProofOfWork::WorkPackage m_current; |
||||
|
EthashProofOfWork::WorkPackage m_previous; |
||||
|
|
||||
|
bool m_stale = false; |
||||
|
|
||||
|
string m_job; |
||||
|
string m_previousJob; |
||||
|
EthashAux::FullType m_dag; |
||||
|
|
||||
|
boost::asio::io_service m_io_service; |
||||
|
tcp::socket m_socket; |
||||
|
|
||||
|
boost::asio::streambuf m_requestBuffer; |
||||
|
boost::asio::streambuf m_responseBuffer; |
||||
|
|
||||
|
boost::asio::deadline_timer * p_worktimer; |
||||
|
|
||||
|
}; |
Loading…
Reference in new issue